diff options
63 files changed, 3321 insertions, 550 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 8622e019fa..206bf7ea45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -980,7 +980,7 @@ S: Supported F: block/rbd.c Sheepdog -M: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp> +M: Hitoshi Mitake <mitake.hitoshi@lab.ntt.co.jp> M: Liu Yuan <namei.unix@gmail.com> L: sheepdog@lists.wpkg.org S: Supported @@ -2246,7 +2246,7 @@ int bdrv_commit(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - + if (!bs->backing_hd) { return -ENOTSUP; } diff --git a/block/archipelago.c b/block/archipelago.c index 34f72dc5a5..22a7daaa41 100644 --- a/block/archipelago.c +++ b/block/archipelago.c @@ -57,6 +57,7 @@ #include "qapi/qmp/qint.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qjson.h" +#include "qemu/atomic.h" #include <inttypes.h> #include <xseg/xseg.h> @@ -214,7 +215,7 @@ static void xseg_request_handler(void *state) xseg_put_request(s->xseg, req, s->srcport); - if ((__sync_add_and_fetch(&segreq->ref, -1)) == 0) { + if (atomic_fetch_dec(&segreq->ref) == 1) { if (!segreq->failed) { reqdata->aio_cb->ret = segreq->count; archipelago_finish_aiocb(reqdata); @@ -233,7 +234,7 @@ static void xseg_request_handler(void *state) segreq->count += req->serviced; xseg_put_request(s->xseg, req, s->srcport); - if ((__sync_add_and_fetch(&segreq->ref, -1)) == 0) { + if (atomic_fetch_dec(&segreq->ref) == 1) { if (!segreq->failed) { reqdata->aio_cb->ret = segreq->count; archipelago_finish_aiocb(reqdata); @@ -824,78 +825,47 @@ static int archipelago_aio_segmented_rw(BDRVArchipelagoState *s, ArchipelagoAIOCB *aio_cb, int op) { - int i, ret, segments_nr, last_segment_size; + int ret, segments_nr; + size_t pos = 0; ArchipelagoSegmentedRequest *segreq; - segreq = g_new(ArchipelagoSegmentedRequest, 1); + segreq = g_new0(ArchipelagoSegmentedRequest, 1); if (op == ARCHIP_OP_FLUSH) { segments_nr = 1; - segreq->ref = segments_nr; - segreq->total = count; - segreq->count = 0; - segreq->failed = 0; - ret = archipelago_submit_request(s, 0, count, offset, aio_cb, - segreq, ARCHIP_OP_FLUSH); - if (ret < 0) { - goto err_exit; - } - return 0; + } else { + segments_nr = (int)(count / MAX_REQUEST_SIZE) + \ + ((count % MAX_REQUEST_SIZE) ? 1 : 0); } - - segments_nr = (int)(count / MAX_REQUEST_SIZE) + \ - ((count % MAX_REQUEST_SIZE) ? 1 : 0); - last_segment_size = (int)(count % MAX_REQUEST_SIZE); - - segreq->ref = segments_nr; segreq->total = count; - segreq->count = 0; - segreq->failed = 0; + atomic_mb_set(&segreq->ref, segments_nr); - for (i = 0; i < segments_nr - 1; i++) { - ret = archipelago_submit_request(s, i * MAX_REQUEST_SIZE, - MAX_REQUEST_SIZE, - offset + i * MAX_REQUEST_SIZE, - aio_cb, segreq, op); + while (segments_nr > 1) { + ret = archipelago_submit_request(s, pos, + MAX_REQUEST_SIZE, + offset + pos, + aio_cb, segreq, op); if (ret < 0) { goto err_exit; } + count -= MAX_REQUEST_SIZE; + pos += MAX_REQUEST_SIZE; + segments_nr--; } - - if ((segments_nr > 1) && last_segment_size) { - ret = archipelago_submit_request(s, i * MAX_REQUEST_SIZE, - last_segment_size, - offset + i * MAX_REQUEST_SIZE, - aio_cb, segreq, op); - } else if ((segments_nr > 1) && !last_segment_size) { - ret = archipelago_submit_request(s, i * MAX_REQUEST_SIZE, - MAX_REQUEST_SIZE, - offset + i * MAX_REQUEST_SIZE, - aio_cb, segreq, op); - } else if (segments_nr == 1) { - ret = archipelago_submit_request(s, 0, count, offset, aio_cb, - segreq, op); - } + ret = archipelago_submit_request(s, pos, count, offset + pos, + aio_cb, segreq, op); if (ret < 0) { goto err_exit; } - return 0; err_exit: - __sync_add_and_fetch(&segreq->failed, 1); - if (segments_nr == 1) { - if (__sync_add_and_fetch(&segreq->ref, -1) == 0) { - g_free(segreq); - } - } else { - if ((__sync_add_and_fetch(&segreq->ref, -segments_nr + i)) == 0) { - g_free(segreq); - } + segreq->failed = 1; + if (atomic_fetch_sub(&segreq->ref, segments_nr) == segments_nr) { + g_free(segreq); } - return ret; } diff --git a/block/vmdk.c b/block/vmdk.c index 07cb62ceb7..a1cb91131e 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -834,6 +834,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, ret = vmdk_add_extent(bs, extent_file, true, sectors, 0, 0, 0, 0, 0, &extent, errp); if (ret < 0) { + bdrv_unref(extent_file); return ret; } extent->flat_start_offset = flat_offset << 9; @@ -845,14 +846,15 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, } else { ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp); } + g_free(buf); if (ret) { - g_free(buf); bdrv_unref(extent_file); return ret; } extent = &s->extents[s->num_extents - 1]; } else { error_setg(errp, "Unsupported extent type '%s'", type); + bdrv_unref(extent_file); return -ENOTSUP; } extent->type = g_strdup(type); diff --git a/blockdev.c b/blockdev.c index e37b068e9e..e919566c16 100644 --- a/blockdev.c +++ b/blockdev.c @@ -39,6 +39,7 @@ #include "qapi/qmp/types.h" #include "qapi-visit.h" #include "qapi/qmp-output-visitor.h" +#include "qapi/util.h" #include "sysemu/sysemu.h" #include "block/block_int.h" #include "qmp-commands.h" @@ -274,25 +275,6 @@ static int parse_block_error_action(const char *buf, bool is_read, Error **errp) } } -static inline int parse_enum_option(const char *lookup[], const char *buf, - int max, int def, Error **errp) -{ - int i; - - if (!buf) { - return def; - } - - for (i = 0; i < max; i++) { - if (!strcmp(buf, lookup[i])) { - return i; - } - } - - error_setg(errp, "invalid parameter value: %s", buf); - return def; -} - static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) { if (throttle_conflicting(cfg)) { @@ -456,11 +438,11 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, } detect_zeroes = - parse_enum_option(BlockdevDetectZeroesOptions_lookup, - qemu_opt_get(opts, "detect-zeroes"), - BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, - BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, - &error); + qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, + qemu_opt_get(opts, "detect-zeroes"), + BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, + &error); if (error) { error_propagate(errp, error); goto early_err; diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 33f8d84e68..d725b23312 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -45,8 +45,8 @@ CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +CONFIG_ETSEC=y +CONFIG_LIBDECNUMBER=y # For PReP CONFIG_MC146818RTC=y -CONFIG_ETSEC=y CONFIG_ISA_TESTDEV=y -CONFIG_LIBDECNUMBER=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 37a15b7ce2..bd30d69347 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -46,6 +46,8 @@ CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +CONFIG_ETSEC=y +CONFIG_LIBDECNUMBER=y # For pSeries CONFIG_XICS=$(CONFIG_PSERIES) CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) @@ -58,4 +60,3 @@ CONFIG_I82374=y CONFIG_I8257=y CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y -CONFIG_LIBDECNUMBER=y @@ -430,15 +430,50 @@ static int cpu_common_post_load(void *opaque, int version_id) return 0; } +static int cpu_common_pre_load(void *opaque) +{ + CPUState *cpu = opaque; + + cpu->exception_index = 0; + + return 0; +} + +static bool cpu_common_exception_index_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return cpu->exception_index != 0; +} + +static const VMStateDescription vmstate_cpu_common_exception_index = { + .name = "cpu_common/exception_index", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(exception_index, CPUState), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_cpu_common = { .name = "cpu_common", .version_id = 1, .minimum_version_id = 1, + .pre_load = cpu_common_pre_load, .post_load = cpu_common_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(halted, CPUState), VMSTATE_UINT32(interrupt_request, CPUState), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_cpu_common_exception_index, + .needed = cpu_common_exception_index_needed, + } , { + /* empty */ + } } }; diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 2238f39579..593fbc5525 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -94,10 +94,13 @@ struct pflash_t { void *storage; }; +static int pflash_post_load(void *opaque, int version_id); + static const VMStateDescription vmstate_pflash = { .name = "pflash_cfi01", .version_id = 1, .minimum_version_id = 1, + .post_load = pflash_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8(wcycle, pflash_t), VMSTATE_UINT8(cmd, pflash_t), @@ -209,11 +212,11 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) switch (boff & 0xFF) { case 0: resp = pfl->ident0; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + DPRINTF("%s: Manufacturer Code %04x\n", __func__, resp); break; case 1: resp = pfl->ident1; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + DPRINTF("%s: Device ID Code %04x\n", __func__, resp); break; default: DPRINTF("%s: Read Device Information offset=%x\n", __func__, @@ -982,3 +985,14 @@ MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl) { return &fl->mem; } + +static int pflash_post_load(void *opaque, int version_id) +{ + pflash_t *pfl = opaque; + + if (!pfl->ro) { + DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name); + pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs); + } + return 0; +} diff --git a/hw/core/loader.c b/hw/core/loader.c index 193f0f8400..597b117db3 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -89,6 +89,27 @@ int load_image(const char *filename, uint8_t *addr) return size; } +/* return the size or -1 if error */ +ssize_t load_image_size(const char *filename, void *addr, size_t size) +{ + int fd; + ssize_t actsize; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + return -1; + } + + actsize = read(fd, addr, size); + if (actsize < 0) { + close(fd); + return -1; + } + close(fd); + + return actsize; +} + /* read()-like version */ ssize_t read_targphys(const char *name, int fd, hwaddr dst_addr, size_t nbytes) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index d43aa49eb8..652af99001 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2063,6 +2063,7 @@ static int qxl_init_primary(PCIDevice *dev) qxl->id = 0; qxl_init_ramsize(qxl); + vga->vbe_size = qxl->vgamem_size; vga->vram_size_mb = qxl->vga.vram_size >> 20; vga_common_init(vga, OBJECT(dev), true); vga_init(vga, OBJECT(dev), diff --git a/hw/display/vga.c b/hw/display/vga.c index 948265a748..f24b48ba95 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -576,6 +576,93 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } +/* + * Sanity check vbe register writes. + * + * As we don't have a way to signal errors to the guest in the bochs + * dispi interface we'll go adjust the registers to the closest valid + * value. + */ +static void vbe_fixup_regs(VGACommonState *s) +{ + uint16_t *r = s->vbe_regs; + uint32_t bits, linelength, maxy, offset; + + if (!(r[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + /* vbe is turned off -- nothing to do */ + return; + } + + /* check depth */ + switch (r[VBE_DISPI_INDEX_BPP]) { + case 4: + case 8: + case 16: + case 24: + case 32: + bits = r[VBE_DISPI_INDEX_BPP]; + break; + case 15: + bits = 16; + break; + default: + bits = r[VBE_DISPI_INDEX_BPP] = 8; + break; + } + + /* check width */ + r[VBE_DISPI_INDEX_XRES] &= ~7u; + if (r[VBE_DISPI_INDEX_XRES] == 0) { + r[VBE_DISPI_INDEX_XRES] = 8; + } + if (r[VBE_DISPI_INDEX_XRES] > VBE_DISPI_MAX_XRES) { + r[VBE_DISPI_INDEX_XRES] = VBE_DISPI_MAX_XRES; + } + r[VBE_DISPI_INDEX_VIRT_WIDTH] &= ~7u; + if (r[VBE_DISPI_INDEX_VIRT_WIDTH] > VBE_DISPI_MAX_XRES) { + r[VBE_DISPI_INDEX_VIRT_WIDTH] = VBE_DISPI_MAX_XRES; + } + if (r[VBE_DISPI_INDEX_VIRT_WIDTH] < r[VBE_DISPI_INDEX_XRES]) { + r[VBE_DISPI_INDEX_VIRT_WIDTH] = r[VBE_DISPI_INDEX_XRES]; + } + + /* check height */ + linelength = r[VBE_DISPI_INDEX_VIRT_WIDTH] * bits / 8; + maxy = s->vbe_size / linelength; + if (r[VBE_DISPI_INDEX_YRES] == 0) { + r[VBE_DISPI_INDEX_YRES] = 1; + } + if (r[VBE_DISPI_INDEX_YRES] > VBE_DISPI_MAX_YRES) { + r[VBE_DISPI_INDEX_YRES] = VBE_DISPI_MAX_YRES; + } + if (r[VBE_DISPI_INDEX_YRES] > maxy) { + r[VBE_DISPI_INDEX_YRES] = maxy; + } + + /* check offset */ + if (r[VBE_DISPI_INDEX_X_OFFSET] > VBE_DISPI_MAX_XRES) { + r[VBE_DISPI_INDEX_X_OFFSET] = VBE_DISPI_MAX_XRES; + } + if (r[VBE_DISPI_INDEX_Y_OFFSET] > VBE_DISPI_MAX_YRES) { + r[VBE_DISPI_INDEX_Y_OFFSET] = VBE_DISPI_MAX_YRES; + } + offset = r[VBE_DISPI_INDEX_X_OFFSET] * bits / 8; + offset += r[VBE_DISPI_INDEX_Y_OFFSET] * linelength; + if (offset + r[VBE_DISPI_INDEX_YRES] * linelength > s->vbe_size) { + r[VBE_DISPI_INDEX_Y_OFFSET] = 0; + offset = r[VBE_DISPI_INDEX_X_OFFSET] * bits / 8; + if (offset + r[VBE_DISPI_INDEX_YRES] * linelength > s->vbe_size) { + r[VBE_DISPI_INDEX_X_OFFSET] = 0; + offset = 0; + } + } + + /* update vga state */ + r[VBE_DISPI_INDEX_VIRT_HEIGHT] = maxy; + s->vbe_line_offset = linelength; + s->vbe_start_addr = offset / 4; +} + static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) { VGACommonState *s = opaque; @@ -610,7 +697,7 @@ uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) val = s->vbe_regs[s->vbe_index]; } } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { - val = s->vram_size / (64 * 1024); + val = s->vbe_size / (64 * 1024); } else { val = 0; } @@ -645,22 +732,13 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) } break; case VBE_DISPI_INDEX_XRES: - if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) { - s->vbe_regs[s->vbe_index] = val; - } - break; case VBE_DISPI_INDEX_YRES: - if (val <= VBE_DISPI_MAX_YRES) { - s->vbe_regs[s->vbe_index] = val; - } - break; case VBE_DISPI_INDEX_BPP: - if (val == 0) - val = 8; - if (val == 4 || val == 8 || val == 15 || - val == 16 || val == 24 || val == 32) { - s->vbe_regs[s->vbe_index] = val; - } + case VBE_DISPI_INDEX_VIRT_WIDTH: + case VBE_DISPI_INDEX_X_OFFSET: + case VBE_DISPI_INDEX_Y_OFFSET: + s->vbe_regs[s->vbe_index] = val; + vbe_fixup_regs(s); break; case VBE_DISPI_INDEX_BANK: if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { @@ -677,19 +755,11 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { int h, shift_control; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = - s->vbe_regs[VBE_DISPI_INDEX_XRES]; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = - s->vbe_regs[VBE_DISPI_INDEX_YRES]; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 0; s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0; s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0; - - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) - s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1; - else - s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * - ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); - s->vbe_start_addr = 0; + s->vbe_regs[VBE_DISPI_INDEX_ENABLE] |= VBE_DISPI_ENABLED; + vbe_fixup_regs(s); /* clear the screen (should be done in BIOS) */ if (!(val & VBE_DISPI_NOCLEARMEM)) { @@ -738,40 +808,6 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) s->vbe_regs[s->vbe_index] = val; vga_update_memory_access(s); break; - case VBE_DISPI_INDEX_VIRT_WIDTH: - { - int w, h, line_offset; - - if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES]) - return; - w = val; - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) - line_offset = w >> 1; - else - line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); - h = s->vram_size / line_offset; - /* XXX: support weird bochs semantics ? */ - if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES]) - return; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h; - s->vbe_line_offset = line_offset; - } - break; - case VBE_DISPI_INDEX_X_OFFSET: - case VBE_DISPI_INDEX_Y_OFFSET: - { - int x; - s->vbe_regs[s->vbe_index] = val; - s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]; - x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET]; - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) - s->vbe_start_addr += x >> 1; - else - s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); - s->vbe_start_addr >>= 2; - } - break; default: break; } @@ -2289,6 +2325,9 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->vram_size <<= 1; } s->vram_size_mb = s->vram_size >> 20; + if (!s->vbe_size) { + s->vbe_size = s->vram_size; + } s->is_vbe_vmstate = 1; memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size); diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 641f8f41d2..bbc0cb2ad8 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -93,6 +93,7 @@ typedef struct VGACommonState { MemoryRegion vram_vbe; uint32_t vram_size; uint32_t vram_size_mb; /* property */ + uint32_t vbe_size; uint32_t latch; bool has_chain4_alias; MemoryRegion chain4_alias; diff --git a/hw/ide/core.c b/hw/ide/core.c index b48127f921..191f89321e 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -75,19 +75,29 @@ static void put_le16(uint16_t *p, unsigned int v) *p = cpu_to_le16(v); } +static void ide_identify_size(IDEState *s) +{ + uint16_t *p = (uint16_t *)s->identify_data; + put_le16(p + 60, s->nb_sectors); + put_le16(p + 61, s->nb_sectors >> 16); + put_le16(p + 100, s->nb_sectors); + put_le16(p + 101, s->nb_sectors >> 16); + put_le16(p + 102, s->nb_sectors >> 32); + put_le16(p + 103, s->nb_sectors >> 48); +} + static void ide_identify(IDEState *s) { uint16_t *p; unsigned int oldsize; IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; + p = (uint16_t *)s->identify_data; if (s->identify_set) { - memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); - return; + goto fill_buffer; } + memset(p, 0, sizeof(s->identify_data)); - memset(s->io_buffer, 0, 512); - p = (uint16_t *)s->io_buffer; put_le16(p + 0, 0x0040); put_le16(p + 1, s->cylinders); put_le16(p + 3, s->heads); @@ -116,8 +126,8 @@ static void ide_identify(IDEState *s) put_le16(p + 58, oldsize >> 16); if (s->mult_sectors) put_le16(p + 59, 0x100 | s->mult_sectors); - put_le16(p + 60, s->nb_sectors); - put_le16(p + 61, s->nb_sectors >> 16); + /* *(p + 60) := nb_sectors -- see ide_identify_size */ + /* *(p + 61) := nb_sectors >> 16 -- see ide_identify_size */ put_le16(p + 62, 0x07); /* single word dma0-2 supported */ put_le16(p + 63, 0x07); /* mdma0-2 supported */ put_le16(p + 64, 0x03); /* pio3-4 supported */ @@ -162,10 +172,10 @@ static void ide_identify(IDEState *s) } put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ put_le16(p + 93, 1 | (1 << 14) | 0x2000); - put_le16(p + 100, s->nb_sectors); - put_le16(p + 101, s->nb_sectors >> 16); - put_le16(p + 102, s->nb_sectors >> 32); - put_le16(p + 103, s->nb_sectors >> 48); + /* *(p + 100) := nb_sectors -- see ide_identify_size */ + /* *(p + 101) := nb_sectors >> 16 -- see ide_identify_size */ + /* *(p + 102) := nb_sectors >> 32 -- see ide_identify_size */ + /* *(p + 103) := nb_sectors >> 48 -- see ide_identify_size */ if (dev && dev->conf.physical_block_size) put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf)); @@ -180,21 +190,23 @@ static void ide_identify(IDEState *s) put_le16(p + 169, 1); /* TRIM support */ } - memcpy(s->identify_data, p, sizeof(s->identify_data)); + ide_identify_size(s); s->identify_set = 1; + +fill_buffer: + memcpy(s->io_buffer, p, sizeof(s->identify_data)); } static void ide_atapi_identify(IDEState *s) { uint16_t *p; + p = (uint16_t *)s->identify_data; if (s->identify_set) { - memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); - return; + goto fill_buffer; } + memset(p, 0, sizeof(s->identify_data)); - memset(s->io_buffer, 0, 512); - p = (uint16_t *)s->io_buffer; /* Removable CDROM, 50us response, 12 byte packets */ put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0)); padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ @@ -230,11 +242,36 @@ static void ide_atapi_identify(IDEState *s) } put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ + if (s->wwn) { + put_le16(p + 84, (1 << 8)); /* supports WWN for words 108-111 */ + put_le16(p + 87, (1 << 8)); /* WWN enabled */ + } + #ifdef USE_DMA_CDROM put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ #endif - memcpy(s->identify_data, p, sizeof(s->identify_data)); + + if (s->wwn) { + /* LE 16-bit words 111-108 contain 64-bit World Wide Name */ + put_le16(p + 108, s->wwn >> 48); + put_le16(p + 109, s->wwn >> 32); + put_le16(p + 110, s->wwn >> 16); + put_le16(p + 111, s->wwn); + } + s->identify_set = 1; + +fill_buffer: + memcpy(s->io_buffer, p, sizeof(s->identify_data)); +} + +static void ide_cfata_identify_size(IDEState *s) +{ + uint16_t *p = (uint16_t *)s->identify_data; + put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */ + put_le16(p + 8, s->nb_sectors); /* Sectors per card */ + put_le16(p + 60, s->nb_sectors); /* Total LBA sectors */ + put_le16(p + 61, s->nb_sectors >> 16); /* Total LBA sectors */ } static void ide_cfata_identify(IDEState *s) @@ -242,10 +279,10 @@ static void ide_cfata_identify(IDEState *s) uint16_t *p; uint32_t cur_sec; - p = (uint16_t *) s->identify_data; - if (s->identify_set) + p = (uint16_t *)s->identify_data; + if (s->identify_set) { goto fill_buffer; - + } memset(p, 0, sizeof(s->identify_data)); cur_sec = s->cylinders * s->heads * s->sectors; @@ -254,8 +291,8 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 1, s->cylinders); /* Default cylinders */ put_le16(p + 3, s->heads); /* Default heads */ put_le16(p + 6, s->sectors); /* Default sectors per track */ - put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */ - put_le16(p + 8, s->nb_sectors); /* Sectors per card */ + /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */ + /* *(p + 8) := nb_sectors -- see ide_cfata_identify_size */ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ put_le16(p + 22, 0x0004); /* ECC bytes */ padstr((char *) (p + 23), s->version, 8); /* Firmware Revision */ @@ -276,8 +313,8 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 58, cur_sec >> 16); /* Current capacity */ if (s->mult_sectors) /* Multiple sector setting */ put_le16(p + 59, 0x100 | s->mult_sectors); - put_le16(p + 60, s->nb_sectors); /* Total LBA sectors */ - put_le16(p + 61, s->nb_sectors >> 16); /* Total LBA sectors */ + /* *(p + 60) := nb_sectors -- see ide_cfata_identify_size */ + /* *(p + 61) := nb_sectors >> 16 -- see ide_cfata_identify_size */ put_le16(p + 63, 0x0203); /* Multiword DMA capability */ put_le16(p + 64, 0x0001); /* Flow Control PIO support */ put_le16(p + 65, 0x0096); /* Min. Multiword DMA cycle */ @@ -297,6 +334,7 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 160, 0x8100); /* Power requirement */ put_le16(p + 161, 0x8001); /* CF command set */ + ide_cfata_identify_size(s); s->identify_set = 1; fill_buffer: @@ -2115,6 +2153,28 @@ static bool ide_cd_is_medium_locked(void *opaque) return ((IDEState *)opaque)->tray_locked; } +static void ide_resize_cb(void *opaque) +{ + IDEState *s = opaque; + uint64_t nb_sectors; + + if (!s->identify_set) { + return; + } + + bdrv_get_geometry(s->bs, &nb_sectors); + s->nb_sectors = nb_sectors; + + /* Update the identify data buffer. */ + if (s->drive_kind == IDE_CFATA) { + ide_cfata_identify_size(s); + } else { + /* IDE_CD uses a different set of callbacks entirely. */ + assert(s->drive_kind != IDE_CD); + ide_identify_size(s); + } +} + static const BlockDevOps ide_cd_block_ops = { .change_media_cb = ide_cd_change_cb, .eject_request_cb = ide_cd_eject_request_cb, @@ -2122,6 +2182,10 @@ static const BlockDevOps ide_cd_block_ops = { .is_medium_locked = ide_cd_is_medium_locked, }; +static const BlockDevOps ide_hd_block_ops = { + .resize_cb = ide_resize_cb, +}; + int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial, const char *model, uint64_t wwn, @@ -2158,6 +2222,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, error_report("Can't use a read-only drive"); return -1; } + bdrv_set_dev_ops(bs, &ide_hd_block_ops, s); } if (serial) { pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), serial); diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index ff6051defe..b4273aa171 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -123,13 +123,22 @@ static void cuda_update_irq(CUDAState *s) } } +static uint64_t get_tb(uint64_t freq) +{ + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + freq, get_ticks_per_sec()); +} + static unsigned int get_counter(CUDATimer *s) { int64_t d; unsigned int counter; + uint64_t tb_diff; + + /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup. */ + tb_diff = get_tb(s->frequency) - s->load_time; + d = (tb_diff * 0xBF401675E5DULL) / (s->frequency << 24); - d = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->load_time, - CUDA_TIMER_FREQ, get_ticks_per_sec()); if (s->index == 0) { /* the timer goes down from latch to -1 (period of latch + 2) */ if (d <= (s->counter_value + 1)) { @@ -147,7 +156,7 @@ static unsigned int get_counter(CUDATimer *s) static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) { CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val); - ti->load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ti->load_time = get_tb(s->frequency); ti->counter_value = val; cuda_timer_update(s, ti, ti->load_time); } @@ -688,6 +697,8 @@ static void cuda_realizefn(DeviceState *dev, Error **errp) struct tm tm; s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s); + s->timers[0].frequency = s->frequency; + s->timers[1].frequency = s->frequency; qemu_get_timedate(&tm, 0); s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; @@ -713,6 +724,11 @@ static void cuda_initfn(Object *obj) DEVICE(obj), "adb.0"); } +static Property cuda_properties[] = { + DEFINE_PROP_UINT64("frequency", CUDAState, frequency, 0), + DEFINE_PROP_END_OF_LIST() +}; + static void cuda_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -720,6 +736,7 @@ static void cuda_class_init(ObjectClass *oc, void *data) dc->realize = cuda_realizefn; dc->reset = cuda_reset; dc->vmsd = &vmstate_cuda; + dc->props = cuda_properties; } static const TypeInfo cuda_type_info = { diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 47f45f5d2c..e0f1e88f54 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -42,6 +42,7 @@ typedef struct MacIOState void *dbdma; MemoryRegion *pic_mem; MemoryRegion *escc_mem; + uint64_t frequency; } MacIOState; #define OLDWORLD_MACIO(obj) \ @@ -243,13 +244,18 @@ static void timer_write(void *opaque, hwaddr addr, uint64_t value, static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) { uint32_t value = 0; + uint64_t systime = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint64_t kltime; + + kltime = muldiv64(systime, 4194300, get_ticks_per_sec() * 4); + kltime = muldiv64(kltime, 18432000, 1048575); switch (addr) { case 0x38: - value = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + value = kltime; break; case 0x3c: - value = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 32; + value = kltime >> 32; break; } @@ -346,12 +352,19 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; } +static Property macio_properties[] = { + DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), + DEFINE_PROP_END_OF_LIST() +}; + static void macio_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_APPLE; k->class_id = PCI_CLASS_OTHERS << 8; + dc->props = macio_properties; } static const TypeInfo macio_oldworld_type_info = { @@ -398,6 +411,8 @@ void macio_init(PCIDevice *d, macio_state->escc_mem = escc_mem; /* Note: this code is strongly inspirated from the corresponding code in PearPC */ + qdev_prop_set_uint64(DEVICE(&macio_state->cuda), "frequency", + macio_state->frequency); qdev_init_nofail(DEVICE(d)); } diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 2d47df6930..23c47d397c 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -72,7 +72,14 @@ typedef uint64_t vlan_bd_t; #define VLAN_RXQ_BD_OFF 0 #define VLAN_FILTER_BD_OFF 8 #define VLAN_RX_BDS_OFF 16 -#define VLAN_MAX_BUFS ((SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8) +/* + * The final 8 bytes of the buffer list is a counter of frames dropped + * because there was not a buffer in the buffer list capable of holding + * the frame. We must avoid it, or the operating system will report garbage + * for this statistic. + */ +#define VLAN_RX_BDS_LEN (SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF - 8) +#define VLAN_MAX_BUFS (VLAN_RX_BDS_LEN / 8) #define TYPE_VIO_SPAPR_VLAN_DEVICE "spapr-vlan" #define VIO_SPAPR_VLAN_DEVICE(obj) \ @@ -119,7 +126,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, do { buf_ptr += 8; - if (buf_ptr >= SPAPR_TCE_PAGE_SIZE) { + if (buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { buf_ptr = VLAN_RX_BDS_OFF; } @@ -397,7 +404,7 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, do { dev->add_buf_ptr += 8; - if (dev->add_buf_ptr >= SPAPR_TCE_PAGE_SIZE) { + if (dev->add_buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { dev->add_buf_ptr = VLAN_RX_BDS_OFF; } diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 170b10b766..d35f8a3121 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -26,6 +26,7 @@ #include "hw/nvram/openbios_firmware_abi.h" #include "sysemu/sysemu.h" #include "hw/ppc/mac.h" +#include <zlib.h> /* debug NVR */ //#define DEBUG_NVR @@ -39,29 +40,6 @@ #define DEF_SYSTEM_SIZE 0xc10 -/* Direct access to NVRAM */ -uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr) -{ - uint32_t ret; - - if (addr < s->size) { - ret = s->data[addr]; - } else { - ret = -1; - } - NVR_DPRINTF("read addr %04" PRIx32 " val %" PRIx8 "\n", addr, ret); - - return ret; -} - -void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val) -{ - NVR_DPRINTF("write addr %04" PRIx32 " val %" PRIx8 "\n", addr, val); - if (addr < s->size) { - s->data[addr] = val; - } -} - /* macio style NVRAM device */ static void macio_nvram_writeb(void *opaque, hwaddr addr, uint64_t value, unsigned size) @@ -89,6 +67,10 @@ static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, static const MemoryRegionOps macio_nvram_ops = { .read = macio_nvram_readb, .write = macio_nvram_writeb, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 1, .endianness = DEVICE_BIG_ENDIAN, }; @@ -156,15 +138,16 @@ static void macio_nvram_register_types(void) } /* Set up a system OpenBIOS NVRAM partition */ -void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len) +static void pmac_format_nvram_partition_of(MacIONVRAMState *nvr, int off, + int len) { unsigned int i; - uint32_t start = 0, end; + uint32_t start = off, end; struct OpenBIOS_nvpart_v1 *part_header; // OpenBIOS nvram variables // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)nvr->data; + part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; part_header->signature = OPENBIOS_PART_SYSTEM; pstrcpy(part_header->name, sizeof(part_header->name), "system"); @@ -192,4 +175,39 @@ void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len) OpenBIOS_finish_partition(part_header, end - start); } +#define OSX_NVRAM_SIGNATURE (0x5A) + +/* Set up a Mac OS X NVRAM partition */ +static void pmac_format_nvram_partition_osx(MacIONVRAMState *nvr, int off, + int len) +{ + uint32_t start = off; + struct OpenBIOS_nvpart_v1 *part_header; + unsigned char *data = &nvr->data[start]; + + /* empty partition */ + part_header = (struct OpenBIOS_nvpart_v1 *)data; + part_header->signature = OSX_NVRAM_SIGNATURE; + pstrcpy(part_header->name, sizeof(part_header->name), "wwwwwwwwwwww"); + + OpenBIOS_finish_partition(part_header, len); + + /* Generation */ + stl_be_p(&data[20], 2); + + /* Adler32 checksum */ + stl_be_p(&data[16], adler32(0, &data[20], len - 20)); +} + +/* Set up NVRAM with OF and OSX partitions */ +void pmac_format_nvram_partition(MacIONVRAMState *nvr, int len) +{ + /* + * Mac OS X expects side "B" of the flash at the second half of NVRAM, + * so we use half of the chip for OF and the other half for a free OSX + * partition. + */ + pmac_format_nvram_partition_of(nvr, 0, len / 2); + pmac_format_nvram_partition_osx(nvr, len / 2, len / 2); +} type_init(macio_nvram_register_types) diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index 762ebdd8fe..f573875baf 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -142,6 +142,7 @@ typedef struct APBState { IOMMUState iommu; uint32_t pci_control[16]; uint32_t pci_irq_map[8]; + uint32_t pci_err_irq_map[4]; uint32_t obio_irq_map[32]; qemu_irq *pbm_irqs; qemu_irq *ivec_irqs; @@ -437,7 +438,7 @@ static void apb_config_writel (void *opaque, hwaddr addr, pbm_check_irqs(s); } break; - case 0x1000 ... 0x1080: /* OBIO interrupt control */ + case 0x1000 ... 0x107f: /* OBIO interrupt control */ if (addr & 4) { unsigned int ino = ((addr & 0xff) >> 3); s->obio_irq_map[ino] &= PBM_PCI_IMR_MASK; @@ -515,13 +516,20 @@ static uint64_t apb_config_readl (void *opaque, val = 0; } break; - case 0x1000 ... 0x1080: /* OBIO interrupt control */ + case 0x1000 ... 0x107f: /* OBIO interrupt control */ if (addr & 4) { val = s->obio_irq_map[(addr & 0xff) >> 3]; } else { val = 0; } break; + case 0x1080 ... 0x108f: /* PCI bus error */ + if (addr & 4) { + val = s->pci_err_irq_map[(addr & 0xf) >> 3]; + } else { + val = 0; + } + break; case 0x2000 ... 0x202f: /* PCI control */ val = s->pci_control[(addr & 0x3f) >> 2]; break; @@ -753,6 +761,9 @@ static int pci_pbm_init_device(SysBusDevice *dev) for (i = 0; i < 8; i++) { s->pci_irq_map[i] = (0x1f << 6) | (i << 2); } + for (i = 0; i < 2; i++) { + s->pci_err_irq_map[i] = (0x1f << 6) | 0x30; + } for (i = 0; i < 32; i++) { s->obio_irq_map[i] = ((0x1f << 6) | 0x20) + i; } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index c1faf9ce27..aff2b9a566 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -57,6 +57,7 @@ typedef struct CUDATimer { uint16_t counter_value; int64_t load_time; int64_t next_irq_time; + uint64_t frequency; QEMUTimer *timer; } CUDATimer; @@ -97,6 +98,7 @@ typedef struct CUDAState { CUDATimer timers[2]; uint32_t tick_offset; + uint64_t frequency; uint8_t last_b; uint8_t last_acr; @@ -178,6 +180,4 @@ typedef struct MacIONVRAMState { } MacIONVRAMState; void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); -uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr); -void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val); #endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 1ec4bb490b..8453bfa284 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -176,6 +176,8 @@ static void ppc_core99_init(MachineState *machine) SysBusDevice *s; DeviceState *dev; int *token = g_new(int, 1); + hwaddr nvram_addr = 0xFFF04000; + uint64_t tbfreq; linux_boot = (kernel_filename != NULL); @@ -372,6 +374,14 @@ static void ppc_core99_init(MachineState *machine) pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; } + + /* Timebase Frequency */ + if (kvm_enabled()) { + tbfreq = kvmppc_get_tbfreq(); + } else { + tbfreq = TBFREQ; + } + /* init basic PC hardware */ escc_mem = escc_init(0, pic[0x25], pic[0x24], serial_hds[0], serial_hds[1], ESCC_CLOCK, 4); @@ -385,6 +395,7 @@ static void ppc_core99_init(MachineState *machine) qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE DMA */ + qdev_prop_set_uint64(dev, "frequency", tbfreq); macio_init(macio, pic_mem, escc_bar); /* We only emulate 2 out of 3 IDE controllers for now */ @@ -426,11 +437,18 @@ static void ppc_core99_init(MachineState *machine) } /* The NewWorld NVRAM is not located in the MacIO device */ +#ifdef CONFIG_KVM + if (kvm_enabled() && getpagesize() > 4096) { + /* We can't combine read-write and read-only in a single page, so + move the NVRAM out of ROM again for KVM */ + nvram_addr = 0xFFE00000; + } +#endif dev = qdev_create(NULL, TYPE_MACIO_NVRAM); qdev_prop_set_uint32(dev, "size", 0x2000); qdev_prop_set_uint32(dev, "it_shift", 1); qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xFFF04000); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, nvram_addr); nvr = MACIO_NVRAM(dev); pmac_format_nvram_partition(nvr, 0x2000); /* No PCI init: the BIOS will do it */ @@ -461,28 +479,34 @@ static void ppc_core99_init(MachineState *machine) #ifdef CONFIG_KVM uint8_t *hypercall; - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); hypercall = g_malloc(16); kvmppc_get_hypercall(env, hypercall, 16); fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); #endif - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, TBFREQ); } + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq); /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, CLOCKFREQ); fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_NVRAM_ADDR, nvram_addr); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } +static int core99_kvm_type(const char *arg) +{ + /* Always force PR KVM */ + return 2; +} + static QEMUMachine core99_machine = { .name = "mac99", .desc = "Mac99 based PowerMAC", .init = ppc_core99_init, .max_cpus = MAX_CPUS, .default_boot_order = "cd", + .kvm_type = core99_kvm_type, }; static void core99_machine_init(void) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index cd9bdbc53e..630a9f9c36 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -103,6 +103,7 @@ static void ppc_heathrow_init(MachineState *machine) uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; + uint64_t tbfreq; linux_boot = (kernel_filename != NULL); @@ -250,6 +251,13 @@ static void ppc_heathrow_init(MachineState *machine) } } + /* Timebase Frequency */ + if (kvm_enabled()) { + tbfreq = kvmppc_get_tbfreq(); + } else { + tbfreq = TBFREQ; + } + /* init basic PC hardware */ if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { hw_error("Only 6xx bus is supported on heathrow machine\n"); @@ -278,6 +286,7 @@ static void ppc_heathrow_init(MachineState *machine) qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE-0 DMA */ qdev_connect_gpio_out(dev, 3, pic[0x0E]); /* IDE-1 */ qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE-1 DMA */ + qdev_prop_set_uint64(dev, "frequency", tbfreq); macio_init(macio, pic_mem, escc_bar); macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), @@ -330,15 +339,13 @@ static void ppc_heathrow_init(MachineState *machine) #ifdef CONFIG_KVM uint8_t *hypercall; - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); hypercall = g_malloc(16); kvmppc_get_hypercall(env, hypercall, 16); fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); #endif - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, TBFREQ); } + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq); /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, CLOCKFREQ); fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ); @@ -346,6 +353,12 @@ static void ppc_heathrow_init(MachineState *machine) qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } +static int heathrow_kvm_type(const char *arg) +{ + /* Always force PR KVM */ + return 2; +} + static QEMUMachine heathrow_machine = { .name = "g3beige", .desc = "Heathrow based PowerMAC", @@ -355,6 +368,7 @@ static QEMUMachine heathrow_machine = { .is_default = 1, #endif .default_boot_order = "cd", /* TOFIX "cad" when Mac floppy is implemented */ + .kvm_type = heathrow_kvm_type, }; static void heathrow_machine_init(void) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 5cb452f234..2ab4460f04 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -71,6 +71,7 @@ */ #define FDT_MAX_SIZE 0x40000 #define RTAS_MAX_SIZE 0x10000 +#define RTAS_MAX_ADDR 0x80000000 /* RTAS must stay below that */ #define FW_MAX_SIZE 0x400000 #define FW_FILE_NAME "slof.bin" #define FW_OVERHEAD 0x2800000 @@ -80,7 +81,7 @@ #define TIMEBASE_FREQ 512000000ULL -#define MAX_CPUS 256 +#define MAX_CPUS 255 #define PHANDLE_XICP 0x00001111 @@ -283,6 +284,19 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, return (p - prop) * sizeof(uint32_t); } +static hwaddr spapr_node0_size(void) +{ + if (nb_numa_nodes) { + int i; + for (i = 0; i < nb_numa_nodes; ++i) { + if (numa_info[i].node_mem) { + return MIN(pow2floor(numa_info[i].node_mem), ram_size); + } + } + } + return ram_size; +} + #define _FDT(exp) \ do { \ int ret = (exp); \ @@ -319,6 +333,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, QemuOpts *opts = qemu_opts_find(qemu_find_opts("smp-opts"), NULL); unsigned sockets = opts ? qemu_opt_get_number(opts, "sockets", 0) : 0; uint32_t cpus_per_socket = sockets ? (smp_cpus / sockets) : 1; + char *buf; add_str(hypertas, "hcall-pft"); add_str(hypertas, "hcall-term"); @@ -348,6 +363,29 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)"))); _FDT((fdt_property_string(fdt, "compatible", "qemu,pseries"))); + /* + * Add info to guest to indentify which host is it being run on + * and what is the uuid of the guest + */ + if (kvmppc_get_host_model(&buf)) { + _FDT((fdt_property_string(fdt, "host-model", buf))); + g_free(buf); + } + if (kvmppc_get_host_serial(&buf)) { + _FDT((fdt_property_string(fdt, "host-serial", buf))); + g_free(buf); + } + + buf = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1], + qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], + qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], + qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], + qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], + qemu_uuid[14], qemu_uuid[15]); + + _FDT((fdt_property_string(fdt, "vm,uuid", buf))); + g_free(buf); + _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); @@ -502,6 +540,15 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX))); + /* + * According to PAPR, rtas ibm,os-term, does not gaurantee a return + * back to the guest cpu. + * + * While an additional ibm,extended-os-term property indicates that + * rtas call return will always occur. Set this property. + */ + _FDT((fdt_property(fdt, "ibm,extended-os-term", NULL, 0))); + _FDT((fdt_end_node(fdt))); /* interrupt controller */ @@ -597,72 +644,75 @@ int spapr_h_cas_compose_response(target_ulong addr, target_ulong size) return 0; } -static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) +static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, + hwaddr size) { - uint32_t associativity[] = {cpu_to_be32(0x4), cpu_to_be32(0x0), - cpu_to_be32(0x0), cpu_to_be32(0x0), - cpu_to_be32(0x0)}; + uint32_t associativity[] = { + cpu_to_be32(0x4), /* length */ + cpu_to_be32(0x0), cpu_to_be32(0x0), + cpu_to_be32(0x0), cpu_to_be32(nodeid) + }; char mem_name[32]; - hwaddr node0_size, mem_start, node_size; uint64_t mem_reg_property[2]; - int i, off; + int off; - /* memory node(s) */ - if (nb_numa_nodes > 1 && numa_info[0].node_mem < ram_size) { - node0_size = numa_info[0].node_mem; - } else { - node0_size = ram_size; - } + mem_reg_property[0] = cpu_to_be64(start); + mem_reg_property[1] = cpu_to_be64(size); - /* RMA */ - mem_reg_property[0] = 0; - mem_reg_property[1] = cpu_to_be64(spapr->rma_size); - off = fdt_add_subnode(fdt, 0, "memory@0"); + sprintf(mem_name, "memory@" TARGET_FMT_lx, start); + off = fdt_add_subnode(fdt, 0, mem_name); _FDT(off); _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property, sizeof(mem_reg_property)))); _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, sizeof(associativity)))); +} + +static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) +{ + hwaddr mem_start, node_size; + int i, nb_nodes = nb_numa_nodes; + NodeInfo *nodes = numa_info; + NodeInfo ramnode; + + /* No NUMA nodes, assume there is just one node with whole RAM */ + if (!nb_numa_nodes) { + nb_nodes = 1; + ramnode.node_mem = ram_size; + nodes = &ramnode; + } - /* RAM: Node 0 */ - if (node0_size > spapr->rma_size) { - mem_reg_property[0] = cpu_to_be64(spapr->rma_size); - mem_reg_property[1] = cpu_to_be64(node0_size - spapr->rma_size); - - sprintf(mem_name, "memory@" TARGET_FMT_lx, spapr->rma_size); - off = fdt_add_subnode(fdt, 0, mem_name); - _FDT(off); - _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); - _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property, - sizeof(mem_reg_property)))); - _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, - sizeof(associativity)))); - } - - /* RAM: Node 1 and beyond */ - mem_start = node0_size; - for (i = 1; i < nb_numa_nodes; i++) { - mem_reg_property[0] = cpu_to_be64(mem_start); + for (i = 0, mem_start = 0; i < nb_nodes; ++i) { + if (!nodes[i].node_mem) { + continue; + } if (mem_start >= ram_size) { node_size = 0; } else { - node_size = numa_info[i].node_mem; + node_size = nodes[i].node_mem; if (node_size > ram_size - mem_start) { node_size = ram_size - mem_start; } } - mem_reg_property[1] = cpu_to_be64(node_size); - associativity[3] = associativity[4] = cpu_to_be32(i); - sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start); - off = fdt_add_subnode(fdt, 0, mem_name); - _FDT(off); - _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); - _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property, - sizeof(mem_reg_property)))); - _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, - sizeof(associativity)))); - mem_start += node_size; + if (!mem_start) { + /* ppc_spapr_init() checks for rma_size <= node0_size already */ + spapr_populate_memory_node(fdt, i, 0, spapr->rma_size); + mem_start += spapr->rma_size; + node_size -= spapr->rma_size; + } + for ( ; node_size; ) { + hwaddr sizetmp = pow2floor(node_size); + + /* mem_start != 0 here */ + if (ctzl(mem_start) < ctzl(sizetmp)) { + sizetmp = 1ULL << ctzl(mem_start); + } + + spapr_populate_memory_node(fdt, i, mem_start, sizetmp); + node_size -= sizetmp; + mem_start += sizetmp; + } } return 0; @@ -746,6 +796,7 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + g_free(bootlist); g_free(fdt); } @@ -792,25 +843,38 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* Update the RMA size if necessary */ if (spapr->vrma_adjust) { - hwaddr node0_size = (nb_numa_nodes > 1) ? - numa_info[0].node_mem : ram_size; - spapr->rma_size = kvmppc_rma_size(node0_size, spapr->htab_shift); + spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), + spapr->htab_shift); } } static void ppc_spapr_reset(void) { PowerPCCPU *first_ppc_cpu; + uint32_t rtas_limit; /* Reset the hash table & recalc the RMA */ spapr_reset_htab(spapr); qemu_devices_reset(); + /* + * We place the device tree and RTAS just below either the top of the RMA, + * or just below 2GB, whichever is lowere, so that it can be + * processed with 32-bit real mode code if necessary + */ + rtas_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR); + spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE; + spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE; + /* Load the fdt */ spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr, spapr->rtas_size); + /* Copy RTAS over */ + cpu_physical_memory_write(spapr->rtas_addr, spapr->rtas_blob, + spapr->rtas_size); + /* Set up the entry state */ first_ppc_cpu = POWERPC_CPU(first_cpu); first_ppc_cpu->env.gpr[3] = spapr->fdt_addr; @@ -1227,10 +1291,10 @@ static void ppc_spapr_init(MachineState *machine) MemoryRegion *rma_region; void *rma = NULL; hwaddr rma_alloc_size; - hwaddr node0_size = (nb_numa_nodes > 1) ? numa_info[0].node_mem : ram_size; + hwaddr node0_size = spapr_node0_size(); uint32_t initrd_base = 0; long kernel_size = 0, initrd_size = 0; - long load_limit, rtas_limit, fw_size; + long load_limit, fw_size; bool kernel_le = false; char *filename; @@ -1275,13 +1339,8 @@ static void ppc_spapr_init(MachineState *machine) exit(1); } - /* We place the device tree and RTAS just below either the top of the RMA, - * or just below 2GB, whichever is lowere, so that it can be - * processed with 32-bit real mode code if necessary */ - rtas_limit = MIN(spapr->rma_size, 0x80000000); - spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE; - spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE; - load_limit = spapr->fdt_addr - FW_OVERHEAD; + /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ + load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; /* We aim for a hash table of size 1/128 the size of RAM. The * normal rule of thumb is 1/64 the size of RAM, but that's much @@ -1349,14 +1408,14 @@ static void ppc_spapr_init(MachineState *machine) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); - spapr->rtas_size = load_image_targphys(filename, spapr->rtas_addr, - rtas_limit - spapr->rtas_addr); - if (spapr->rtas_size < 0) { + spapr->rtas_size = get_image_size(filename); + spapr->rtas_blob = g_malloc(spapr->rtas_size); + if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) { hw_error("qemu: could not load LPAR rtas '%s'\n", filename); exit(1); } if (spapr->rtas_size > RTAS_MAX_SIZE) { - hw_error("RTAS too big ! 0x%lx bytes (max is 0x%x)\n", + hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n", spapr->rtas_size, RTAS_MAX_SIZE); exit(1); } @@ -1378,7 +1437,6 @@ static void ppc_spapr_init(MachineState *machine) spapr_create_nvram(spapr); /* Set up PCI */ - spapr_pci_msi_init(spapr, SPAPR_PCI_MSI_WINDOW); spapr_pci_rtas_init(); phb = spapr_create_phb(spapr, 0); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 467858ce05..86514472fe 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -712,10 +712,10 @@ static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_set_mode_resouce_le(PowerPCCPU *cpu, - target_ulong mflags, - target_ulong value1, - target_ulong value2) +static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, + target_ulong mflags, + target_ulong value1, + target_ulong value2) { CPUState *cs; @@ -743,10 +743,10 @@ static target_ulong h_set_mode_resouce_le(PowerPCCPU *cpu, return H_UNSUPPORTED_FLAG; } -static target_ulong h_set_mode_resouce_addr_trans_mode(PowerPCCPU *cpu, - target_ulong mflags, - target_ulong value1, - target_ulong value2) +static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, + target_ulong mflags, + target_ulong value1, + target_ulong value2) { CPUState *cs; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); @@ -794,11 +794,11 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr, switch (resource) { case H_SET_MODE_RESOURCE_LE: - ret = h_set_mode_resouce_le(cpu, args[0], args[2], args[3]); + ret = h_set_mode_resource_le(cpu, args[0], args[2], args[3]); break; case H_SET_MODE_RESOURCE_ADDR_TRANS_MODE: - ret = h_set_mode_resouce_addr_trans_mode(cpu, args[0], - args[2], args[3]); + ret = h_set_mode_resource_addr_trans_mode(cpu, args[0], + args[2], args[3]); break; } diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 9ed39a93b7..ad0da7fdc4 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -262,7 +262,6 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, unsigned int irq, max_irqs = 0, num = 0; sPAPRPHBState *phb = NULL; PCIDevice *pdev = NULL; - bool msix = false; spapr_pci_msi *msi; int *config_addr_key; @@ -300,7 +299,12 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, } xics_free(spapr->icp, msi->first_irq, msi->num); - spapr_msi_setmsg(pdev, 0, msix, 0, num); + if (msi_present(pdev)) { + spapr_msi_setmsg(pdev, 0, false, 0, num); + } + if (msix_present(pdev)) { + spapr_msi_setmsg(pdev, 0, true, 0, num); + } g_hash_table_remove(phb->msi, &config_addr); trace_spapr_pci_msi("Released MSIs", config_addr); @@ -341,7 +345,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, } /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ - spapr_msi_setmsg(pdev, spapr->msi_win_addr, ret_intr_type == RTAS_TYPE_MSIX, + spapr_msi_setmsg(pdev, SPAPR_PCI_MSI_WINDOW, ret_intr_type == RTAS_TYPE_MSIX, irq, req_num); /* Add MSI device to cache */ @@ -465,34 +469,6 @@ static const MemoryRegionOps spapr_msi_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; -void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) -{ - uint64_t window_size = 4096; - - /* - * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, - * we need to allocate some memory to catch those writes coming - * from msi_notify()/msix_notify(). - * As MSIMessage:addr is going to be the same and MSIMessage:data - * is going to be a VIRQ number, 4 bytes of the MSI MR will only - * be used. - * - * For KVM we want to ensure that this memory is a full page so that - * our memory slot is of page size granularity. - */ -#ifdef CONFIG_KVM - if (kvm_enabled()) { - window_size = getpagesize(); - } -#endif - - spapr->msi_win_addr = addr; - memory_region_init_io(&spapr->msiwindow, NULL, &spapr_msi_ops, spapr, - "msi", window_size); - memory_region_add_subregion(get_system_memory(), spapr->msi_win_addr, - &spapr->msiwindow); -} - /* * PHB PCI device */ @@ -512,6 +488,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) char *namebuf; int i; PCIBus *bus; + uint64_t msi_window_size = 4096; if (sphb->index != -1) { hwaddr windows_base; @@ -604,6 +581,28 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) address_space_init(&sphb->iommu_as, &sphb->iommu_root, sphb->dtbusname); + /* + * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, + * we need to allocate some memory to catch those writes coming + * from msi_notify()/msix_notify(). + * As MSIMessage:addr is going to be the same and MSIMessage:data + * is going to be a VIRQ number, 4 bytes of the MSI MR will only + * be used. + * + * For KVM we want to ensure that this memory is a full page so that + * our memory slot is of page size granularity. + */ +#ifdef CONFIG_KVM + if (kvm_enabled()) { + msi_window_size = getpagesize(); + } +#endif + + memory_region_init_io(&sphb->msiwindow, NULL, &spapr_msi_ops, spapr, + "msi", msi_window_size); + memory_region_add_subregion(&sphb->iommu_root, SPAPR_PCI_MSI_WINDOW, + &sphb->msiwindow); + pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 9ba1ba69f9..2ec2a8e4d1 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -277,6 +277,19 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, rtas_st(rets, 0, ret); } +static void rtas_ibm_os_term(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong ret = 0; + + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); + + rtas_st(rets, 0, ret); +} + static struct rtas_call { const char *name; spapr_rtas_fn fn; @@ -404,6 +417,8 @@ static void core_rtas_register_types(void) spapr_rtas_register(RTAS_IBM_SET_SYSTEM_PARAMETER, "ibm,set-system-parameter", rtas_ibm_set_system_parameter); + spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term", + rtas_ibm_os_term); } type_init(core_rtas_register_types) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index bbe4c5fb85..73ced1f5f8 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -499,6 +499,7 @@ enum xhci_flags { XHCI_FLAG_USE_MSI = 1, XHCI_FLAG_USE_MSI_X, XHCI_FLAG_SS_FIRST, + XHCI_FLAG_FORCE_PCIE_ENDCAP, }; static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, @@ -3626,7 +3627,8 @@ static int usb_xhci_initfn(struct PCIDevice *dev) PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, &xhci->mem); - if (pci_bus_is_express(dev->bus)) { + if (pci_bus_is_express(dev->bus) || + xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { ret = pcie_endpoint_cap_init(dev, 0xa0); assert(ret >= 0); } @@ -3855,6 +3857,8 @@ static Property xhci_properties[] = { DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), DEFINE_PROP_BIT("superspeed-ports-first", XHCIState, flags, XHCI_FLAG_SS_FIRST, true), + DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, + XHCI_FLAG_FORCE_PCIE_ENDCAP, false), DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index a39cb42290..77316d55c8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -330,6 +330,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .value = "off",\ },\ {\ + .driver = "nec-usb-xhci",\ + .property = "force-pcie-endcap",\ + .value = "on",\ + },\ + {\ .driver = "pci-serial",\ .property = "prog_if",\ .value = stringify(0),\ diff --git a/include/hw/loader.h b/include/hw/loader.h index 00c9117ee0..9190387b8e 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -13,6 +13,7 @@ */ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); /* deprecated */ +ssize_t load_image_size(const char *filename, void *addr, size_t size); int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 32f0aa7d10..4ea2a0d14a 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -70,7 +70,7 @@ struct sPAPRPHBState { MemoryRegion memspace, iospace; hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size; - MemoryRegion memwindow, iowindow; + MemoryRegion memwindow, iowindow, msiwindow; uint32_t dma_liobn; AddressSpace iommu_as; diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 7e16e2e06c..14efd0ca31 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -92,7 +92,7 @@ enum { #define FW_CFG_PPC_IS_KVM (FW_CFG_ARCH_LOCAL + 0x05) #define FW_CFG_PPC_KVM_HC (FW_CFG_ARCH_LOCAL + 0x06) #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) -/* OpenBIOS has FW_CFG_PPC_NVRAM_ADDR as +0x08 */ +#define FW_CFG_PPC_NVRAM_ADDR (FW_CFG_ARCH_LOCAL + 0x08) #define FW_CFG_PPC_BUSFREQ (FW_CFG_ARCH_LOCAL + 0x09) #define PPC_SERIAL_MM_BAUDBASE 399193 diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index bbba51a703..749daf4dd7 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -13,8 +13,6 @@ struct sPAPRNVRAM; typedef struct sPAPREnvironment { struct VIOsPAPRBus *vio_bus; QLIST_HEAD(, sPAPRPHBState) phbs; - hwaddr msi_win_addr; - MemoryRegion msiwindow; struct sPAPRNVRAM *nvram; XICSState *icp; @@ -24,7 +22,8 @@ typedef struct sPAPREnvironment { hwaddr rma_size; int vrma_adjust; hwaddr fdt_addr, rtas_addr; - long rtas_size; + ssize_t rtas_size; + void *rtas_blob; void *fdt_skel; target_ulong entry_point; uint64_t rtc_offset; @@ -382,9 +381,8 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_GET_SENSOR_STATE (RTAS_TOKEN_BASE + 0x1D) #define RTAS_IBM_CONFIGURE_CONNECTOR (RTAS_TOKEN_BASE + 0x1E) #define RTAS_IBM_OS_TERM (RTAS_TOKEN_BASE + 0x1F) -#define RTAS_IBM_EXTENDED_OS_TERM (RTAS_TOKEN_BASE + 0x20) -#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x21) +#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x20) /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 diff --git a/include/qapi/util.h b/include/qapi/util.h new file mode 100644 index 0000000000..de9238bf95 --- /dev/null +++ b/include/qapi/util.h @@ -0,0 +1,17 @@ +/* + * QAPI util functions + * + * Copyright Fujitsu, Inc. 2014 + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QAPI_UTIL_H +#define QAPI_UTIL_H + +int qapi_enum_parse(const char *lookup[], const char *buf, + int max, int def, Error **errp); + +#endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 174ea36afa..d2000af9c3 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -303,6 +303,8 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cpu); int kvm_check_extension(KVMState *s, unsigned int extension); +int kvm_vm_check_extension(KVMState *s, unsigned int extension); + #define kvm_vm_enable_cap(s, capability, cap_flags, ...) \ ({ \ struct kvm_enable_cap cap = { \ @@ -493,6 +493,19 @@ int kvm_check_extension(KVMState *s, unsigned int extension) return ret; } +int kvm_vm_check_extension(KVMState *s, unsigned int extension) +{ + int ret; + + ret = kvm_vm_ioctl(s, KVM_CHECK_EXTENSION, extension); + if (ret < 0) { + /* VM wide version not implemented, use global one instead */ + ret = kvm_check_extension(s, extension); + } + + return ret; +} + static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, bool assign, uint32_t size, bool datamatch) { diff --git a/linux-user/signal.c b/linux-user/signal.c index 26929c59de..e11b20887e 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4325,15 +4325,7 @@ badframe: return 0; } -#elif defined(TARGET_PPC) && !defined(TARGET_PPC64) - -/* FIXME: Many of the structures are defined for both PPC and PPC64, but - the signal handling is different enough that we haven't implemented - support for PPC64 yet. Hence the restriction above. - - There are various #if'd blocks for code for TARGET_PPC64. These - blocks should go away so that we can successfully run 32-bit and - 64-bit binaries on a QEMU configured for PPC64. */ +#elif defined(TARGET_PPC) /* Size of dummy stack frame allocated when calling signal handler. See arch/powerpc/include/asm/ptrace.h. */ @@ -4343,6 +4335,33 @@ badframe: #define SIGNAL_FRAMESIZE 64 #endif +/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; + on 64-bit PPC, sigcontext and mcontext are one and the same. */ +struct target_mcontext { + target_ulong mc_gregs[48]; + /* Includes fpscr. */ + uint64_t mc_fregs[33]; + target_ulong mc_pad[2]; + /* We need to handle Altivec and SPE at the same time, which no + kernel needs to do. Fortunately, the kernel defines this bit to + be Altivec-register-large all the time, rather than trying to + twiddle it based on the specific platform. */ + union { + /* SPE vector registers. One extra for SPEFSCR. */ + uint32_t spe[33]; + /* Altivec vector registers. The packing of VSCR and VRSAVE + varies depending on whether we're PPC64 or not: PPC64 splits + them apart; PPC32 stuffs them together. */ +#if defined(TARGET_PPC64) +#define QEMU_NVRREG 34 +#else +#define QEMU_NVRREG 33 +#endif + ppc_avr_t altivec[QEMU_NVRREG]; +#undef QEMU_NVRREG + } mc_vregs __attribute__((__aligned__(16))); +}; + /* See arch/powerpc/include/asm/sigcontext.h. */ struct target_sigcontext { target_ulong _unused[4]; @@ -4353,7 +4372,9 @@ struct target_sigcontext { target_ulong handler; target_ulong oldmask; target_ulong regs; /* struct pt_regs __user * */ - /* TODO: PPC64 includes extra bits here. */ +#if defined(TARGET_PPC64) + struct target_mcontext mcontext; +#endif }; /* Indices for target_mcontext.mc_gregs, below. @@ -4408,32 +4429,6 @@ enum { TARGET_PT_REGS_COUNT = 44 }; -/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; - on 64-bit PPC, sigcontext and mcontext are one and the same. */ -struct target_mcontext { - target_ulong mc_gregs[48]; - /* Includes fpscr. */ - uint64_t mc_fregs[33]; - target_ulong mc_pad[2]; - /* We need to handle Altivec and SPE at the same time, which no - kernel needs to do. Fortunately, the kernel defines this bit to - be Altivec-register-large all the time, rather than trying to - twiddle it based on the specific platform. */ - union { - /* SPE vector registers. One extra for SPEFSCR. */ - uint32_t spe[33]; - /* Altivec vector registers. The packing of VSCR and VRSAVE - varies depending on whether we're PPC64 or not: PPC64 splits - them apart; PPC32 stuffs them together. */ -#if defined(TARGET_PPC64) -#define QEMU_NVRREG 34 -#else -#define QEMU_NVRREG 33 -#endif - ppc_avr_t altivec[QEMU_NVRREG]; -#undef QEMU_NVRREG - } mc_vregs __attribute__((__aligned__(16))); -}; struct target_ucontext { target_ulong tuc_flags; @@ -4447,7 +4442,7 @@ struct target_ucontext { target_sigset_t tuc_sigmask; #if defined(TARGET_PPC64) target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ - struct target_sigcontext tuc_mcontext; + struct target_sigcontext tuc_sigcontext; #else int32_t tuc_maskext[30]; int32_t tuc_pad2[3]; @@ -4462,12 +4457,41 @@ struct target_sigframe { int32_t abigap[56]; }; +#if defined(TARGET_PPC64) + +#define TARGET_TRAMP_SIZE 6 + +struct target_rt_sigframe { + /* sys_rt_sigreturn requires the ucontext be the first field */ + struct target_ucontext uc; + target_ulong _unused[2]; + uint32_t trampoline[TARGET_TRAMP_SIZE]; + target_ulong pinfo; /* struct siginfo __user * */ + target_ulong puc; /* void __user * */ + struct target_siginfo info; + /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ + char abigap[288]; +} __attribute__((aligned(16))); + +#else + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; int32_t abigap[56]; }; +#endif + +#if defined(TARGET_PPC64) + +struct target_func_ptr { + target_ulong entry; + target_ulong toc; +}; + +#endif + /* We use the mc_pad field for the signal return trampoline. */ #define tramp mc_pad @@ -4491,8 +4515,7 @@ static target_ulong get_sigframe(struct target_sigaction *ka, return newsp; } -static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame, - int sigret) +static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) { target_ulong msr = env->msr; int i; @@ -4559,11 +4582,14 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame, /* Store MSR. */ __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); +} +static void encode_trampoline(int sigret, uint32_t *tramp) +{ /* Set up the sigreturn trampoline: li r0,sigret; sc. */ if (sigret) { - __put_user(0x38000000UL | sigret, &frame->tramp[0]); - __put_user(0x44000002UL, &frame->tramp[1]); + __put_user(0x38000000 | sigret, &tramp[0]); + __put_user(0x44000002, &tramp[1]); } } @@ -4655,6 +4681,9 @@ static void setup_frame(int sig, struct target_sigaction *ka, target_ulong frame_addr, newsp; int err = 0; int signal; +#if defined(TARGET_PPC64) + struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; +#endif frame_addr = get_sigframe(ka, env, sizeof(*frame)); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) @@ -4665,7 +4694,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, __put_user(ka->_sa_handler, &sc->handler); __put_user(set->sig[0], &sc->oldmask); -#if defined(TARGET_PPC64) +#if TARGET_ABI_BITS == 64 __put_user(set->sig[0] >> 32, &sc->_unused[3]); #else __put_user(set->sig[1], &sc->_unused[3]); @@ -4674,7 +4703,10 @@ static void setup_frame(int sig, struct target_sigaction *ka, __put_user(sig, &sc->signal); /* Save user regs. */ - save_user_regs(env, &frame->mctx, TARGET_NR_sigreturn); + save_user_regs(env, &frame->mctx); + + /* Construct the trampoline code on the stack. */ + encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); /* The kernel checks for the presence of a VDSO here. We don't emulate a vdso, so use a sigreturn system call. */ @@ -4694,7 +4726,24 @@ static void setup_frame(int sig, struct target_sigaction *ka, env->gpr[1] = newsp; env->gpr[3] = signal; env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); + +#if defined(TARGET_PPC64) + if (get_ppc64_abi(image) < 2) { + /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); + } else { + /* ELFv2 PPC64 function pointers are entry points, but R12 + * must also be set */ + env->nip = tswapl((target_ulong) ka->_sa_handler); + env->gpr[12] = env->nip; + } +#else env->nip = (target_ulong) ka->_sa_handler; +#endif + /* Signal handlers are entered in big-endian mode. */ env->msr &= ~MSR_LE; @@ -4712,10 +4761,14 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUPPCState *env) { struct target_rt_sigframe *rt_sf; - struct target_mcontext *frame; + uint32_t *trampptr = 0; + struct target_mcontext *mctx = 0; target_ulong rt_sf_addr, newsp = 0; int i, err = 0; int signal; +#if defined(TARGET_PPC64) + struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; +#endif rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) @@ -4733,25 +4786,35 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, &rt_sf->uc.tuc_stack.ss_flags); __put_user(target_sigaltstack_used.ss_size, &rt_sf->uc.tuc_stack.ss_size); +#if !defined(TARGET_PPC64) __put_user(h2g (&rt_sf->uc.tuc_mcontext), &rt_sf->uc.tuc_regs); +#endif for(i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); } - frame = &rt_sf->uc.tuc_mcontext; - save_user_regs(env, frame, TARGET_NR_rt_sigreturn); +#if defined(TARGET_PPC64) + mctx = &rt_sf->uc.tuc_sigcontext.mcontext; + trampptr = &rt_sf->trampoline[0]; +#else + mctx = &rt_sf->uc.tuc_mcontext; + trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; +#endif + + save_user_regs(env, mctx); + encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); /* The kernel checks for the presence of a VDSO here. We don't emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(frame->tramp); + env->lr = (target_ulong) h2g(trampptr); /* Turn off all fp exceptions. */ env->fpscr = 0; /* Create a stack frame for the caller of the handler. */ newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); - __put_user(env->gpr[1], (target_ulong *)(uintptr_t) newsp); + err |= put_user(env->gpr[1], newsp, target_ulong); if (err) goto sigsegv; @@ -4762,7 +4825,24 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, env->gpr[4] = (target_ulong) h2g(&rt_sf->info); env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); env->gpr[6] = (target_ulong) h2g(rt_sf); + +#if defined(TARGET_PPC64) + if (get_ppc64_abi(image) < 2) { + /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); + } else { + /* ELFv2 PPC64 function pointers are entry points, but R12 + * must also be set */ + env->nip = tswapl((target_ulong) ka->_sa_handler); + env->gpr[12] = env->nip; + } +#else env->nip = (target_ulong) ka->_sa_handler; +#endif + /* Signal handlers are entered in big-endian mode. */ env->msr &= ~MSR_LE; @@ -4789,7 +4869,7 @@ long do_sigreturn(CPUPPCState *env) goto sigsegv; #if defined(TARGET_PPC64) - set.sig[0] = sc->oldmask + ((long)(sc->_unused[3]) << 32); + set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); #else __get_user(set.sig[0], &sc->oldmask); __get_user(set.sig[1], &sc->_unused[3]); @@ -4827,10 +4907,11 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) return 1; #if defined(TARGET_PPC64) - fprintf (stderr, "do_setcontext: not implemented\n"); - return 0; + mcp_addr = h2g(ucp) + + offsetof(struct target_ucontext, tuc_sigcontext.mcontext); #else __get_user(mcp_addr, &ucp->tuc_regs); +#endif if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) return 1; @@ -4841,7 +4922,6 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) unlock_user_struct(mcp, mcp_addr, 1); return 0; -#endif } long do_rt_sigreturn(CPUPPCState *env) diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index d14b769cff..2278970690 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 += qmp-event.o +util-obj-y += qapi-util.o diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c new file mode 100644 index 0000000000..1d8fb96eff --- /dev/null +++ b/qapi/qapi-util.c @@ -0,0 +1,34 @@ +/* + * QAPI util functions + * + * Authors: + * Hu Tao <hutao@cn.fujitsu.com> + * Peter Lieven <pl@kamp.de> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/util.h" + +int qapi_enum_parse(const char *lookup[], const char *buf, + int max, int def, Error **errp) +{ + int i; + + if (!buf) { + return def; + } + + for (i = 0; i < max; i++) { + if (!strcmp(buf, lookup[i])) { + return i; + } + } + + error_setg(errp, "invalid parameter value: %s", buf); + return def; +} diff --git a/qemu-char.c b/qemu-char.c index d4f327ab6d..1a8d9aa543 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1160,7 +1160,9 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) if (!s->connected) { /* guest sends data, check for (re-)connect */ pty_chr_update_read_handler_locked(chr); - return 0; + if (!s->connected) { + return 0; + } } return io_channel_send(s->fd, buf, len); } diff --git a/qemu-img.c b/qemu-img.c index ff29ed1c67..91d1ac3d7d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -95,7 +95,8 @@ static void QEMU_NORETURN help(void) " 'cache' is the cache mode used to write the output disk image, the valid\n" " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" " 'directsync' and 'unsafe' (default for convert)\n" - " 'src_cache' in contrast is the cache mode used to read input disk images\n" + " 'src_cache' is the cache mode used to read input disk images, the valid\n" + " options are the same as for the 'cache' option\n" " 'size' is the disk image size in bytes. Optional suffixes\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n" " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n" diff --git a/qemu-img.texi b/qemu-img.texi index cb689483b6..cc4668e64f 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -73,8 +73,9 @@ specifies the cache mode that should be used with the (destination) file. See the documentation of the emulator's @code{-drive cache=...} option for allowed values. @item -T @var{src_cache} -in contrast specifies the cache mode that should be used with the source -file(s). +specifies the cache mode that should be used with the source file(s). See +the documentation of the emulator's @code{-drive cache=...} option for allowed +values. @end table Parameters to snapshot subcommand: @@ -340,7 +341,7 @@ string), then the image is rebased onto no backing file (i.e. it will exist independently of any backing file). @var{cache} specifies the cache mode to be used for @var{filename}, whereas -@var{src_cache} specifies the cache mode for reading the new backing file. +@var{src_cache} specifies the cache mode for reading backing files. There are two different modes in which @code{rebase} can operate: @table @option diff --git a/qemu-nbd.c b/qemu-nbd.c index 626e5844f9..9bc152e6c3 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -18,11 +18,13 @@ #include "qemu-common.h" #include "block/block.h" +#include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qemu/error-report.h" #include "block/snapshot.h" +#include "qapi/util.h" #include <stdarg.h> #include <stdio.h> @@ -37,10 +39,11 @@ #include <libgen.h> #include <pthread.h> -#define SOCKET_PATH "/var/lock/qemu-nbd-%s" -#define QEMU_NBD_OPT_CACHE 1 -#define QEMU_NBD_OPT_AIO 2 -#define QEMU_NBD_OPT_DISCARD 3 +#define SOCKET_PATH "/var/lock/qemu-nbd-%s" +#define QEMU_NBD_OPT_CACHE 1 +#define QEMU_NBD_OPT_AIO 2 +#define QEMU_NBD_OPT_DISCARD 3 +#define QEMU_NBD_OPT_DETECT_ZEROES 4 static NBDExport *exp; static int verbose; @@ -57,45 +60,47 @@ static void usage(const char *name) "Usage: %s [OPTIONS] FILE\n" "QEMU Disk Network Block Device Server\n" "\n" -" -h, --help display this help and exit\n" -" -V, --version output version information and exit\n" +" -h, --help display this help and exit\n" +" -V, --version output version information and exit\n" "\n" "Connection properties:\n" -" -p, --port=PORT port to listen on (default `%d')\n" -" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n" -" -k, --socket=PATH path to the unix socket\n" -" (default '"SOCKET_PATH"')\n" -" -e, --shared=NUM device can be shared by NUM clients (default '1')\n" -" -t, --persistent don't exit on the last connection\n" -" -v, --verbose display extra debugging information\n" +" -p, --port=PORT port to listen on (default `%d')\n" +" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n" +" -k, --socket=PATH path to the unix socket\n" +" (default '"SOCKET_PATH"')\n" +" -e, --shared=NUM device can be shared by NUM clients (default '1')\n" +" -t, --persistent don't exit on the last connection\n" +" -v, --verbose display extra debugging information\n" "\n" "Exposing part of the image:\n" -" -o, --offset=OFFSET offset into the image\n" -" -P, --partition=NUM only expose partition NUM\n" +" -o, --offset=OFFSET offset into the image\n" +" -P, --partition=NUM only expose partition NUM\n" "\n" #ifdef __linux__ "Kernel NBD client support:\n" -" -c, --connect=DEV connect FILE to the local NBD device DEV\n" -" -d, --disconnect disconnect the specified device\n" +" -c, --connect=DEV connect FILE to the local NBD device DEV\n" +" -d, --disconnect disconnect the specified device\n" "\n" #endif "\n" "Block device options:\n" -" -f, --format=FORMAT set image format (raw, qcow2, ...)\n" -" -r, --read-only export read-only\n" -" -s, --snapshot use FILE as an external snapshot, create a temporary\n" -" file with backing_file=FILE, redirect the write to\n" -" the temporary one\n" +" -f, --format=FORMAT set image format (raw, qcow2, ...)\n" +" -r, --read-only export read-only\n" +" -s, --snapshot use FILE as an external snapshot, create a temporary\n" +" file with backing_file=FILE, redirect the write to\n" +" the temporary one\n" " -l, --load-snapshot=SNAPSHOT_PARAM\n" -" load an internal snapshot inside FILE and export it\n" -" as an read-only device, SNAPSHOT_PARAM format is\n" -" 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" -" '[ID_OR_NAME]'\n" -" -n, --nocache disable host cache\n" -" --cache=MODE set cache mode (none, writeback, ...)\n" +" load an internal snapshot inside FILE and export it\n" +" as an read-only device, SNAPSHOT_PARAM format is\n" +" 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" +" '[ID_OR_NAME]'\n" +" -n, --nocache disable host cache\n" +" --cache=MODE set cache mode (none, writeback, ...)\n" #ifdef CONFIG_LINUX_AIO -" --aio=MODE set AIO mode (native or threads)\n" +" --aio=MODE set AIO mode (native or threads)\n" #endif +" --discard=MODE set discard mode (ignore, unmap)\n" +" --detect-zeroes=MODE set detect-zeroes mode (off, on, discard)\n" "\n" "Report bugs to <qemu-devel@nongnu.org>\n" , name, NBD_DEFAULT_PORT, "DEVICE"); @@ -410,6 +415,7 @@ int main(int argc, char **argv) { "aio", 1, NULL, QEMU_NBD_OPT_AIO }, #endif { "discard", 1, NULL, QEMU_NBD_OPT_DISCARD }, + { "detect-zeroes", 1, NULL, QEMU_NBD_OPT_DETECT_ZEROES }, { "shared", 1, NULL, 'e' }, { "format", 1, NULL, 'f' }, { "persistent", 0, NULL, 't' }, @@ -432,6 +438,7 @@ int main(int argc, char **argv) pthread_t client_thread; const char *fmt = NULL; Error *local_err = NULL; + BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -483,6 +490,23 @@ int main(int argc, char **argv) errx(EXIT_FAILURE, "Invalid discard mode `%s'", optarg); } break; + case QEMU_NBD_OPT_DETECT_ZEROES: + detect_zeroes = + qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, + optarg, + BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, + &local_err); + if (local_err) { + errx(EXIT_FAILURE, "Failed to parse detect_zeroes mode: %s", + error_get_pretty(local_err)); + } + if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + !(flags & BDRV_O_UNMAP)) { + errx(EXIT_FAILURE, "setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + } + break; case 'b': bindto = optarg; break; @@ -522,15 +546,18 @@ int main(int argc, char **argv) break; case 'P': partition = strtol(optarg, &end, 0); - if (*end) + if (*end) { errx(EXIT_FAILURE, "Invalid partition `%s'", optarg); - if (partition < 1 || partition > 8) + } + if (partition < 1 || partition > 8) { errx(EXIT_FAILURE, "Invalid partition %d", partition); + } break; case 'k': sockpath = optarg; - if (sockpath[0] != '/') + if (sockpath[0] != '/') { errx(EXIT_FAILURE, "socket path must be absolute\n"); + } break; case 'd': disconnect = true; @@ -550,9 +577,9 @@ int main(int argc, char **argv) case 'f': fmt = optarg; break; - case 't': - persistent = 1; - break; + case 't': + persistent = 1; + break; case 'v': verbose = 1; break; @@ -587,7 +614,7 @@ int main(int argc, char **argv) printf("%s disconnected\n", argv[optind]); - return 0; + return 0; } if (device && !verbose) { @@ -686,6 +713,7 @@ int main(int argc, char **argv) error_get_pretty(local_err)); } + bs->detect_zeroes = detect_zeroes; fd_size = bdrv_getlength(bs); if (partition != -1) { diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index 71a1b97cfc..77554663a7 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -92,6 +92,7 @@ typedef struct X86CPU { bool enforce_cpuid; bool expose_kvm; bool migratable; + bool host_features; /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index fa811a02d1..88b64d8b66 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -592,7 +592,8 @@ static bool lookup_feature(uint32_t *pval, const char *s, const char *e, } static void add_flagname_to_bitmaps(const char *flagname, - FeatureWordArray words) + FeatureWordArray words, + Error **errp) { FeatureWord w; for (w = 0; w < FEATURE_WORDS; w++) { @@ -603,7 +604,7 @@ static void add_flagname_to_bitmaps(const char *flagname, } } if (w == FEATURE_WORDS) { - fprintf(stderr, "CPU feature %s not found\n", flagname); + error_setg(errp, "CPU feature %s not found", flagname); } } @@ -1254,6 +1255,9 @@ void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, } } +static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, + bool migratable_only); + #ifdef CONFIG_KVM static int cpu_x86_fill_model_id(char *str) @@ -1310,26 +1314,23 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data) dc->props = host_x86_cpu_properties; } -static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, - bool migratable_only); - static void host_x86_cpu_initfn(Object *obj) { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; KVMState *s = kvm_state; - FeatureWord w; assert(kvm_enabled()); + /* We can't fill the features array here because we don't know yet if + * "migratable" is true or false. + */ + cpu->host_features = true; + 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); - for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] = - x86_cpu_get_supported_feature_word(w, cpu->migratable); - } object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort); } @@ -1761,9 +1762,9 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, while (featurestr) { char *val; if (featurestr[0] == '+') { - add_flagname_to_bitmaps(featurestr + 1, plus_features); + add_flagname_to_bitmaps(featurestr + 1, plus_features, &local_err); } else if (featurestr[0] == '-') { - add_flagname_to_bitmaps(featurestr + 1, minus_features); + add_flagname_to_bitmaps(featurestr + 1, minus_features, &local_err); } else if ((val = strchr(featurestr, '='))) { *val = 0; val++; feat2prop(featurestr); @@ -1828,6 +1829,13 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, featurestr = strtok(NULL, ","); } + if (cpu->host_features) { + for (w = 0; w < FEATURE_WORDS; w++) { + env->features[w] = + x86_cpu_get_supported_feature_word(w, cpu->migratable); + } + } + for (w = 0; w < FEATURE_WORDS; w++) { env->features[w] |= plus_features[w]; env->features[w] &= ~minus_features[w]; diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 509eae52ff..0cfdc8ab8f 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -28,7 +28,6 @@ DEF_HELPER_2(icbi, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) -DEF_HELPER_3(mulldo, i64, env, i64, i64) DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) DEF_HELPER_4(divde, i64, env, i64, i64, i32) #endif diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index f6e8846707..713d777076 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -24,23 +24,6 @@ #include "helper_regs.h" /*****************************************************************************/ /* Fixed point operations helpers */ -#if defined(TARGET_PPC64) - -uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2) -{ - int64_t th; - uint64_t tl; - - muls64(&tl, (uint64_t *)&th, arg1, arg2); - /* If th != 0 && th != -1, then we had an overflow */ - if (likely((uint64_t)(th + 1) <= 1)) { - env->ov = 0; - } else { - env->so = env->ov = 1; - } - return (int64_t)tl; -} -#endif target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) @@ -238,7 +221,7 @@ target_ulong helper_srad(CPUPPCState *env, target_ulong value, if (likely((uint64_t)shift != 0)) { shift &= 0x3f; ret = (int64_t)value >> shift; - if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { + if (likely(ret >= 0 || (value & ((1ULL << shift) - 1)) == 0)) { env->ca = 0; } else { env->ca = 1; diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 42718f77ae..9c23c6ba0d 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -38,6 +38,7 @@ #include "hw/ppc/ppc.h" #include "sysemu/watchdog.h" #include "trace.h" +#include "exec/gdbstub.h" //#define DEBUG_KVM @@ -72,6 +73,8 @@ static int cap_papr; static int cap_htab_fd; static int cap_fixup_hcalls; +static uint32_t debug_inst_opcode; + /* XXX We have a race condition where we actually have a level triggered * interrupt, but the infrastructure can't expose that yet, so the guest * takes but ignores it, goes to sleep and never gets notified that there's @@ -410,6 +413,38 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) return ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); } +/* e500 supports 2 h/w breakpoint and 2 watchpoint. + * book3s supports only 1 watchpoint, so array size + * of 4 is sufficient for now. + */ +#define MAX_HW_BKPTS 4 + +static struct HWBreakpoint { + target_ulong addr; + int type; +} hw_debug_points[MAX_HW_BKPTS]; + +static CPUWatchpoint hw_watchpoint; + +/* Default there is no breakpoint and watchpoint supported */ +static int max_hw_breakpoint; +static int max_hw_watchpoint; +static int nb_hw_breakpoint; +static int nb_hw_watchpoint; + +static void kvmppc_hw_debug_points_init(CPUPPCState *cenv) +{ + if (cenv->excp_model == POWERPC_EXCP_BOOKE) { + max_hw_breakpoint = 2; + max_hw_watchpoint = 2; + } + + if ((max_hw_breakpoint + max_hw_watchpoint) > MAX_HW_BKPTS) { + fprintf(stderr, "Error initializing h/w breakpoints\n"); + return; + } +} + int kvm_arch_init_vcpu(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -436,6 +471,9 @@ int kvm_arch_init_vcpu(CPUState *cs) break; } + kvm_get_one_reg(cs, KVM_REG_PPC_DEBUG_INST, &debug_inst_opcode); + kvmppc_hw_debug_points_init(cenv); + return ret; } @@ -899,6 +937,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } +static void kvm_sync_excp(CPUPPCState *env, int vector, int ivor) +{ + env->excp_vectors[vector] = env->spr[ivor] + env->spr[SPR_BOOKE_IVPR]; +} + int kvm_arch_get_registers(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -981,35 +1024,57 @@ int kvm_arch_get_registers(CPUState *cs) if (sregs.u.e.features & KVM_SREGS_E_IVOR) { env->spr[SPR_BOOKE_IVOR0] = sregs.u.e.ivor_low[0]; + kvm_sync_excp(env, POWERPC_EXCP_CRITICAL, SPR_BOOKE_IVOR0); env->spr[SPR_BOOKE_IVOR1] = sregs.u.e.ivor_low[1]; + kvm_sync_excp(env, POWERPC_EXCP_MCHECK, SPR_BOOKE_IVOR1); env->spr[SPR_BOOKE_IVOR2] = sregs.u.e.ivor_low[2]; + kvm_sync_excp(env, POWERPC_EXCP_DSI, SPR_BOOKE_IVOR2); env->spr[SPR_BOOKE_IVOR3] = sregs.u.e.ivor_low[3]; + kvm_sync_excp(env, POWERPC_EXCP_ISI, SPR_BOOKE_IVOR3); env->spr[SPR_BOOKE_IVOR4] = sregs.u.e.ivor_low[4]; + kvm_sync_excp(env, POWERPC_EXCP_EXTERNAL, SPR_BOOKE_IVOR4); env->spr[SPR_BOOKE_IVOR5] = sregs.u.e.ivor_low[5]; + kvm_sync_excp(env, POWERPC_EXCP_ALIGN, SPR_BOOKE_IVOR5); env->spr[SPR_BOOKE_IVOR6] = sregs.u.e.ivor_low[6]; + kvm_sync_excp(env, POWERPC_EXCP_PROGRAM, SPR_BOOKE_IVOR6); env->spr[SPR_BOOKE_IVOR7] = sregs.u.e.ivor_low[7]; + kvm_sync_excp(env, POWERPC_EXCP_FPU, SPR_BOOKE_IVOR7); env->spr[SPR_BOOKE_IVOR8] = sregs.u.e.ivor_low[8]; + kvm_sync_excp(env, POWERPC_EXCP_SYSCALL, SPR_BOOKE_IVOR8); env->spr[SPR_BOOKE_IVOR9] = sregs.u.e.ivor_low[9]; + kvm_sync_excp(env, POWERPC_EXCP_APU, SPR_BOOKE_IVOR9); env->spr[SPR_BOOKE_IVOR10] = sregs.u.e.ivor_low[10]; + kvm_sync_excp(env, POWERPC_EXCP_DECR, SPR_BOOKE_IVOR10); env->spr[SPR_BOOKE_IVOR11] = sregs.u.e.ivor_low[11]; + kvm_sync_excp(env, POWERPC_EXCP_FIT, SPR_BOOKE_IVOR11); env->spr[SPR_BOOKE_IVOR12] = sregs.u.e.ivor_low[12]; + kvm_sync_excp(env, POWERPC_EXCP_WDT, SPR_BOOKE_IVOR12); env->spr[SPR_BOOKE_IVOR13] = sregs.u.e.ivor_low[13]; + kvm_sync_excp(env, POWERPC_EXCP_DTLB, SPR_BOOKE_IVOR13); env->spr[SPR_BOOKE_IVOR14] = sregs.u.e.ivor_low[14]; + kvm_sync_excp(env, POWERPC_EXCP_ITLB, SPR_BOOKE_IVOR14); env->spr[SPR_BOOKE_IVOR15] = sregs.u.e.ivor_low[15]; + kvm_sync_excp(env, POWERPC_EXCP_DEBUG, SPR_BOOKE_IVOR15); if (sregs.u.e.features & KVM_SREGS_E_SPE) { env->spr[SPR_BOOKE_IVOR32] = sregs.u.e.ivor_high[0]; + kvm_sync_excp(env, POWERPC_EXCP_SPEU, SPR_BOOKE_IVOR32); env->spr[SPR_BOOKE_IVOR33] = sregs.u.e.ivor_high[1]; + kvm_sync_excp(env, POWERPC_EXCP_EFPDI, SPR_BOOKE_IVOR33); env->spr[SPR_BOOKE_IVOR34] = sregs.u.e.ivor_high[2]; + kvm_sync_excp(env, POWERPC_EXCP_EFPRI, SPR_BOOKE_IVOR34); } if (sregs.u.e.features & KVM_SREGS_E_PM) { env->spr[SPR_BOOKE_IVOR35] = sregs.u.e.ivor_high[3]; + kvm_sync_excp(env, POWERPC_EXCP_EPERFM, SPR_BOOKE_IVOR35); } if (sregs.u.e.features & KVM_SREGS_E_PC) { env->spr[SPR_BOOKE_IVOR36] = sregs.u.e.ivor_high[4]; + kvm_sync_excp(env, POWERPC_EXCP_DOORI, SPR_BOOKE_IVOR36); env->spr[SPR_BOOKE_IVOR37] = sregs.u.e.ivor_high[5]; + kvm_sync_excp(env, POWERPC_EXCP_DOORCI, SPR_BOOKE_IVOR37); } } @@ -1244,6 +1309,259 @@ static int kvmppc_handle_dcr_write(CPUPPCState *env, uint32_t dcrn, uint32_t dat return 0; } +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + /* Mixed endian case is not handled */ + uint32_t sc = debug_inst_opcode; + + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, + sizeof(sc), 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&sc, sizeof(sc), 1)) { + return -EINVAL; + } + + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + uint32_t sc; + + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&sc, sizeof(sc), 0) || + sc != debug_inst_opcode || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, + sizeof(sc), 1)) { + return -EINVAL; + } + + return 0; +} + +static int find_hw_breakpoint(target_ulong addr, int type) +{ + int n; + + assert((nb_hw_breakpoint + nb_hw_watchpoint) + <= ARRAY_SIZE(hw_debug_points)); + + for (n = 0; n < nb_hw_breakpoint + nb_hw_watchpoint; n++) { + if (hw_debug_points[n].addr == addr && + hw_debug_points[n].type == type) { + return n; + } + } + + return -1; +} + +static int find_hw_watchpoint(target_ulong addr, int *flag) +{ + int n; + + n = find_hw_breakpoint(addr, GDB_WATCHPOINT_ACCESS); + if (n >= 0) { + *flag = BP_MEM_ACCESS; + return n; + } + + n = find_hw_breakpoint(addr, GDB_WATCHPOINT_WRITE); + if (n >= 0) { + *flag = BP_MEM_WRITE; + return n; + } + + n = find_hw_breakpoint(addr, GDB_WATCHPOINT_READ); + if (n >= 0) { + *flag = BP_MEM_READ; + return n; + } + + return -1; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + if ((nb_hw_breakpoint + nb_hw_watchpoint) >= ARRAY_SIZE(hw_debug_points)) { + return -ENOBUFS; + } + + hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint].addr = addr; + hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint].type = type; + + switch (type) { + case GDB_BREAKPOINT_HW: + if (nb_hw_breakpoint >= max_hw_breakpoint) { + return -ENOBUFS; + } + + if (find_hw_breakpoint(addr, type) >= 0) { + return -EEXIST; + } + + nb_hw_breakpoint++; + break; + + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_ACCESS: + if (nb_hw_watchpoint >= max_hw_watchpoint) { + return -ENOBUFS; + } + + if (find_hw_breakpoint(addr, type) >= 0) { + return -EEXIST; + } + + nb_hw_watchpoint++; + break; + + default: + return -ENOSYS; + } + + return 0; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + int n; + + n = find_hw_breakpoint(addr, type); + if (n < 0) { + return -ENOENT; + } + + switch (type) { + case GDB_BREAKPOINT_HW: + nb_hw_breakpoint--; + break; + + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_ACCESS: + nb_hw_watchpoint--; + break; + + default: + return -ENOSYS; + } + hw_debug_points[n] = hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint]; + + return 0; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + nb_hw_breakpoint = nb_hw_watchpoint = 0; +} + +void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) +{ + int n; + + /* Software Breakpoint updates */ + if (kvm_sw_breakpoints_active(cs)) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + } + + assert((nb_hw_breakpoint + nb_hw_watchpoint) + <= ARRAY_SIZE(hw_debug_points)); + assert((nb_hw_breakpoint + nb_hw_watchpoint) <= ARRAY_SIZE(dbg->arch.bp)); + + if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; + memset(dbg->arch.bp, 0, sizeof(dbg->arch.bp)); + for (n = 0; n < nb_hw_breakpoint + nb_hw_watchpoint; n++) { + switch (hw_debug_points[n].type) { + case GDB_BREAKPOINT_HW: + dbg->arch.bp[n].type = KVMPPC_DEBUG_BREAKPOINT; + break; + case GDB_WATCHPOINT_WRITE: + dbg->arch.bp[n].type = KVMPPC_DEBUG_WATCH_WRITE; + break; + case GDB_WATCHPOINT_READ: + dbg->arch.bp[n].type = KVMPPC_DEBUG_WATCH_READ; + break; + case GDB_WATCHPOINT_ACCESS: + dbg->arch.bp[n].type = KVMPPC_DEBUG_WATCH_WRITE | + KVMPPC_DEBUG_WATCH_READ; + break; + default: + cpu_abort(cs, "Unsupported breakpoint type\n"); + } + dbg->arch.bp[n].addr = hw_debug_points[n].addr; + } + } +} + +static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + struct kvm_debug_exit_arch *arch_info = &run->debug.arch; + int handle = 0; + int n; + int flag = 0; + + if (cs->singlestep_enabled) { + handle = 1; + } else if (arch_info->status) { + if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { + if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { + n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); + if (n >= 0) { + handle = 1; + } + } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | + KVMPPC_DEBUG_WATCH_WRITE)) { + n = find_hw_watchpoint(arch_info->address, &flag); + if (n >= 0) { + handle = 1; + cs->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_debug_points[n].addr; + hw_watchpoint.flags = flag; + } + } + } + } else if (kvm_find_sw_breakpoint(cs, arch_info->address)) { + handle = 1; + } else { + /* QEMU is not able to handle debug exception, so inject + * program exception to guest; + * Yes program exception NOT debug exception !! + * When QEMU is using debug resources then debug exception must + * be always set. To achieve this we set MSR_DE and also set + * MSRP_DEP so guest cannot change MSR_DE. + * When emulating debug resource for guest we want guest + * to control MSR_DE (enable/disable debug interrupt on need). + * Supporting both configurations are NOT possible. + * So the result is that we cannot share debug resources + * between QEMU and Guest on BOOKE architecture. + * In the current design QEMU gets the priority over guest, + * this means that if QEMU is using debug resources then guest + * cannot use them; + * For software breakpoint QEMU uses a privileged instruction; + * So there cannot be any reason that we are here for guest + * set debug exception, only possibility is guest executed a + * privileged / illegal instruction and that's why we are + * injecting a program interrupt. + */ + + cpu_synchronize_state(cs); + /* env->nip is PC, so increment this by 4 to use + * ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4. + */ + env->nip += 4; + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_INVAL; + ppc_cpu_do_interrupt(cs); + } + + return handle; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -1284,6 +1602,16 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = 0; break; + case KVM_EXIT_DEBUG: + DPRINTF("handle debug exception\n"); + if (kvm_handle_debug(cpu, run)) { + ret = EXCP_DEBUG; + break; + } + /* re-enter, this exception was guest-internal */ + ret = 0; + break; + default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -1369,7 +1697,7 @@ static int read_cpuinfo(const char *field, char *value, int len) } do { - if(!fgets(line, sizeof(line), f)) { + if (!fgets(line, sizeof(line), f)) { break; } if (!strncmp(line, field, field_len)) { @@ -1404,6 +1732,17 @@ uint32_t kvmppc_get_tbfreq(void) return retval; } +bool kvmppc_get_host_serial(char **value) +{ + return g_file_get_contents("/proc/device-tree/system-id", value, NULL, + NULL); +} + +bool kvmppc_get_host_model(char **value) +{ + return g_file_get_contents("/proc/device-tree/model", value, NULL, NULL); +} + /* Try to find a device tree node for a CPU with clock-frequency property */ static int kvmppc_find_cpu_dt(char *buf, int buf_len) { @@ -1496,7 +1835,7 @@ static int kvmppc_get_pvinfo(CPUPPCState *env, struct kvm_ppc_pvinfo *pvinfo) PowerPCCPU *cpu = ppc_env_get_cpu(env); CPUState *cs = CPU(cpu); - if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_PVINFO) && + if (kvm_vm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_PVINFO) && !kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_PVINFO, pvinfo)) { return 0; } @@ -1965,34 +2304,6 @@ void kvm_arch_init_irq_routing(KVMState *s) { } -int kvm_arch_insert_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp) -{ - return -EINVAL; -} - -int kvm_arch_remove_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp) -{ - return -EINVAL; -} - -int kvm_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type) -{ - return -EINVAL; -} - -int kvm_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type) -{ - return -EINVAL; -} - -void kvm_arch_remove_all_hw_breakpoints(void) -{ -} - -void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) -{ -} - struct kvm_get_htab_buf { struct kvm_get_htab_header header; /* diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index d9516e73ef..2e0224c6af 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -19,6 +19,8 @@ uint32_t kvmppc_get_tbfreq(void); uint64_t kvmppc_get_clockfreq(void); uint32_t kvmppc_get_vmx(void); uint32_t kvmppc_get_dfp(void); +bool kvmppc_get_host_model(char **buf); +bool kvmppc_get_host_serial(char **buf); int kvmppc_get_hasidle(CPUPPCState *env); int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level); @@ -60,6 +62,16 @@ static inline uint32_t kvmppc_get_tbfreq(void) return 0; } +static inline bool kvmppc_get_host_model(char **buf) +{ + return false; +} + +static inline bool kvmppc_get_host_serial(char **buf) +{ + return false; +} + static inline uint64_t kvmppc_get_clockfreq(void) { return 0; diff --git a/target-ppc/translate.c b/target-ppc/translate.c index c07bb01a7a..d03daeaa48 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -1128,9 +1128,19 @@ static void gen_mulhwu(DisasContext *ctx) /* mullw mullw. */ static void gen_mullw(DisasContext *ctx) { - tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_ext32s_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)]); +#if defined(TARGET_PPC64) + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mul_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); +#else + tcg_gen_mul_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); +#endif if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1144,7 +1154,11 @@ static void gen_mullwo(DisasContext *ctx) tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); tcg_gen_muls2_i32(t0, t1, t0, t1); - tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); +#if defined(TARGET_PPC64) + tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); +#else + tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], t0); +#endif tcg_gen_sari_i32(t0, t0, 31); tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t1); @@ -1201,8 +1215,20 @@ static void gen_mulld(DisasContext *ctx) /* mulldo mulldo. */ static void gen_mulldo(DisasContext *ctx) { - gen_helper_mulldo(cpu_gpr[rD(ctx->opcode)], cpu_env, - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(t0, t1, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mov_i64(cpu_gpr[rD(ctx->opcode)], t0); + + tcg_gen_sari_i64(t0, t0, 63); + tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1616,18 +1642,17 @@ static void gen_rlwimi(DisasContext *ctx) mb = MB(ctx->opcode); me = ME(ctx->opcode); sh = SH(ctx->opcode); - if (likely(sh == 0 && mb == 0 && me == 31)) { - tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + if (likely(sh == (31-me) && mb <= me)) { + tcg_gen_deposit_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], sh, me - mb + 1); } else { target_ulong mask; TCGv t1; TCGv t0 = tcg_temp_new(); #if defined(TARGET_PPC64) - TCGv_i32 t2 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t2, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_rotli_i32(t2, t2, sh); - tcg_gen_extu_i32_i64(t0, t2); - tcg_temp_free_i32(t2); + tcg_gen_deposit_i64(t0, cpu_gpr[rS(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], 32, 32); + tcg_gen_rotli_i64(t0, t0, sh); #else tcg_gen_rotli_i32(t0, cpu_gpr[rS(ctx->opcode)], sh); #endif @@ -1672,14 +1697,18 @@ static void gen_rlwinm(DisasContext *ctx) tcg_gen_shri_tl(t0, t0, mb); tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], t0); tcg_temp_free(t0); + } else if (likely(mb == 0 && me == 31)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rS(ctx->opcode)]); + tcg_gen_rotli_i32(t0, t0, sh); + tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t0); + tcg_temp_free_i32(t0); } else { TCGv t0 = tcg_temp_new(); #if defined(TARGET_PPC64) - TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t1, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_rotli_i32(t1, t1, sh); - tcg_gen_extu_i32_i64(t0, t1); - tcg_temp_free_i32(t1); + tcg_gen_deposit_i64(t0, cpu_gpr[rS(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], 32, 32); + tcg_gen_rotli_i64(t0, t0, sh); #else tcg_gen_rotli_i32(t0, cpu_gpr[rS(ctx->opcode)], sh); #endif @@ -1698,37 +1727,49 @@ static void gen_rlwinm(DisasContext *ctx) static void gen_rlwnm(DisasContext *ctx) { uint32_t mb, me; - TCGv t0; + mb = MB(ctx->opcode); + me = ME(ctx->opcode); + + if (likely(mb == 0 && me == 31)) { + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rS(ctx->opcode)]); + tcg_gen_andi_i32(t0, t0, 0x1f); + tcg_gen_rotl_i32(t1, t1, t0); + tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } else { + TCGv t0; #if defined(TARGET_PPC64) - TCGv_i32 t1, t2; + TCGv t1; #endif - mb = MB(ctx->opcode); - me = ME(ctx->opcode); - t0 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1f); + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1f); #if defined(TARGET_PPC64) - t1 = tcg_temp_new_i32(); - t2 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(t1, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_trunc_i64_i32(t2, t0); - tcg_gen_rotl_i32(t1, t1, t2); - tcg_gen_extu_i32_i64(t0, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + t1 = tcg_temp_new_i64(); + tcg_gen_deposit_i64(t1, cpu_gpr[rS(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], 32, 32); + tcg_gen_rotl_i64(t0, t1, t0); + tcg_temp_free_i64(t1); #else - tcg_gen_rotl_i32(t0, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_rotl_i32(t0, cpu_gpr[rS(ctx->opcode)], t0); #endif - if (unlikely(mb != 0 || me != 31)) { + if (unlikely(mb != 0 || me != 31)) { #if defined(TARGET_PPC64) - mb += 32; - me += 32; + mb += 32; + me += 32; #endif - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], t0, MASK(mb, me)); - } else { - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], t0, MASK(mb, me)); + } else { + tcg_gen_andi_tl(t0, t0, MASK(32, 63)); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + } + tcg_temp_free(t0); } - tcg_temp_free(t0); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } @@ -1924,7 +1965,7 @@ static void gen_srawi(DisasContext *ctx) TCGv dst = cpu_gpr[rA(ctx->opcode)]; TCGv src = cpu_gpr[rS(ctx->opcode)]; if (sh == 0) { - tcg_gen_mov_tl(dst, src); + tcg_gen_ext32s_tl(dst, src); tcg_gen_movi_tl(cpu_ca, 0); } else { TCGv t0; diff --git a/tests/Makefile b/tests/Makefile index 469c0a5e44..d5db97ba63 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -299,6 +299,7 @@ libqos-obj-y += tests/libqos/i2c.o libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o libqos-pc-obj-y += tests/libqos/malloc-pc.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o +libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o @@ -320,7 +321,7 @@ tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o -tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o +tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o diff --git a/tests/ide-test.c b/tests/ide-test.c index ffce6ed669..b7a97e9362 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -126,6 +126,8 @@ static void ide_test_start(const char *cmdline_fmt, ...) static void ide_test_quit(void) { + pc_alloc_uninit(guest_malloc); + guest_malloc = NULL; qtest_end(); } diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index be1d97f8bb..f4218c6451 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -17,45 +17,294 @@ #include "hw/nvram/fw_cfg.h" #include "qemu-common.h" +#include "qemu/queue.h" #include <glib.h> #define PAGE_SIZE (4096) +#define MLIST_ENTNAME entries +typedef QTAILQ_HEAD(MemList, MemBlock) MemList; +typedef struct MemBlock { + QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME; + uint64_t size; + uint64_t addr; +} MemBlock; + typedef struct PCAlloc { QGuestAllocator alloc; - + PCAllocOpts opts; uint64_t start; uint64_t end; + + MemList used; + MemList free; } PCAlloc; -static uint64_t pc_alloc(QGuestAllocator *allocator, size_t size) +static MemBlock *mlist_new(uint64_t addr, uint64_t size) { - PCAlloc *s = container_of(allocator, PCAlloc, alloc); - uint64_t addr; + MemBlock *block; + + if (!size) { + return NULL; + } + block = g_malloc0(sizeof(MemBlock)); + block->addr = addr; + block->size = size; - size += (PAGE_SIZE - 1); - size &= -PAGE_SIZE; + return block; +} + +static void mlist_delete(MemList *list, MemBlock *node) +{ + g_assert(list && node); + QTAILQ_REMOVE(list, node, MLIST_ENTNAME); + g_free(node); +} + +static MemBlock *mlist_find_key(MemList *head, uint64_t addr) +{ + MemBlock *node; + QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { + if (node->addr == addr) { + return node; + } + } + return NULL; +} + +static MemBlock *mlist_find_space(MemList *head, uint64_t size) +{ + MemBlock *node; + + QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { + if (node->size >= size) { + return node; + } + } + return NULL; +} + +static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr) +{ + MemBlock *node; + g_assert(head && insr); + + QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { + if (insr->addr < node->addr) { + QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME); + return insr; + } + } + + QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME); + return insr; +} + +static inline uint64_t mlist_boundary(MemBlock *node) +{ + return node->size + node->addr; +} + +static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right) +{ + g_assert(head && left && right); + + left->size += right->size; + mlist_delete(head, right); + return left; +} + +static void mlist_coalesce(MemList *head, MemBlock *node) +{ + g_assert(node); + MemBlock *left; + MemBlock *right; + char merge; + + do { + merge = 0; + left = QTAILQ_PREV(node, MemList, MLIST_ENTNAME); + right = QTAILQ_NEXT(node, MLIST_ENTNAME); + + /* clowns to the left of me */ + if (left && mlist_boundary(left) == node->addr) { + node = mlist_join(head, left, node); + merge = 1; + } + + /* jokers to the right */ + if (right && mlist_boundary(node) == right->addr) { + node = mlist_join(head, node, right); + merge = 1; + } + + } while (merge); +} + +static uint64_t pc_mlist_fulfill(PCAlloc *s, MemBlock *freenode, uint64_t size) +{ + uint64_t addr; + MemBlock *usednode; - g_assert_cmpint((s->start + size), <=, s->end); + g_assert(freenode); + g_assert_cmpint(freenode->size, >=, size); - addr = s->start; - s->start += size; + addr = freenode->addr; + if (freenode->size == size) { + /* re-use this freenode as our used node */ + QTAILQ_REMOVE(&s->free, freenode, MLIST_ENTNAME); + usednode = freenode; + } else { + /* adjust the free node and create a new used node */ + freenode->addr += size; + freenode->size -= size; + usednode = mlist_new(addr, size); + } + mlist_sort_insert(&s->used, usednode); return addr; } +/* To assert the correctness of the list. + * Used only if PC_ALLOC_PARANOID is set. */ +static void pc_mlist_check(PCAlloc *s) +{ + MemBlock *node; + uint64_t addr = s->start > 0 ? s->start - 1 : 0; + uint64_t next = s->start; + + QTAILQ_FOREACH(node, &s->free, MLIST_ENTNAME) { + g_assert_cmpint(node->addr, >, addr); + g_assert_cmpint(node->addr, >=, next); + addr = node->addr; + next = node->addr + node->size; + } + + addr = s->start > 0 ? s->start - 1 : 0; + next = s->start; + QTAILQ_FOREACH(node, &s->used, MLIST_ENTNAME) { + g_assert_cmpint(node->addr, >, addr); + g_assert_cmpint(node->addr, >=, next); + addr = node->addr; + next = node->addr + node->size; + } +} + +static uint64_t pc_mlist_alloc(PCAlloc *s, uint64_t size) +{ + MemBlock *node; + + node = mlist_find_space(&s->free, size); + if (!node) { + fprintf(stderr, "Out of guest memory.\n"); + g_assert_not_reached(); + } + return pc_mlist_fulfill(s, node, size); +} + +static void pc_mlist_free(PCAlloc *s, uint64_t addr) +{ + MemBlock *node; + + if (addr == 0) { + return; + } + + node = mlist_find_key(&s->used, addr); + if (!node) { + fprintf(stderr, "Error: no record found for an allocation at " + "0x%016" PRIx64 ".\n", + addr); + g_assert_not_reached(); + } + + /* Rip it out of the used list and re-insert back into the free list. */ + QTAILQ_REMOVE(&s->used, node, MLIST_ENTNAME); + mlist_sort_insert(&s->free, node); + mlist_coalesce(&s->free, node); +} + +static uint64_t pc_alloc(QGuestAllocator *allocator, size_t size) +{ + PCAlloc *s = container_of(allocator, PCAlloc, alloc); + uint64_t rsize = size; + uint64_t naddr; + + rsize += (PAGE_SIZE - 1); + rsize &= -PAGE_SIZE; + g_assert_cmpint((s->start + rsize), <=, s->end); + g_assert_cmpint(rsize, >=, size); + + naddr = pc_mlist_alloc(s, rsize); + if (s->opts & PC_ALLOC_PARANOID) { + pc_mlist_check(s); + } + + return naddr; +} + static void pc_free(QGuestAllocator *allocator, uint64_t addr) { + PCAlloc *s = container_of(allocator, PCAlloc, alloc); + + pc_mlist_free(s, addr); + if (s->opts & PC_ALLOC_PARANOID) { + pc_mlist_check(s); + } +} + +/* + * Mostly for valgrind happiness, but it does offer + * a chokepoint for debugging guest memory leaks, too. + */ +void pc_alloc_uninit(QGuestAllocator *allocator) +{ + PCAlloc *s = container_of(allocator, PCAlloc, alloc); + MemBlock *node; + MemBlock *tmp; + PCAllocOpts mask; + + /* Check for guest leaks, and destroy the list. */ + QTAILQ_FOREACH_SAFE(node, &s->used, MLIST_ENTNAME, tmp) { + if (s->opts & (PC_ALLOC_LEAK_WARN | PC_ALLOC_LEAK_ASSERT)) { + fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; " + "size 0x%016" PRIx64 ".\n", + node->addr, node->size); + } + if (s->opts & (PC_ALLOC_LEAK_ASSERT)) { + g_assert_not_reached(); + } + g_free(node); + } + + /* If we have previously asserted that there are no leaks, then there + * should be only one node here with a specific address and size. */ + mask = PC_ALLOC_LEAK_ASSERT | PC_ALLOC_PARANOID; + QTAILQ_FOREACH_SAFE(node, &s->free, MLIST_ENTNAME, tmp) { + if ((s->opts & mask) == mask) { + if ((node->addr != s->start) || + (node->size != s->end - s->start)) { + fprintf(stderr, "Free list is corrupted.\n"); + g_assert_not_reached(); + } + } + + g_free(node); + } + + g_free(s); } -QGuestAllocator *pc_alloc_init(void) +QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags) { PCAlloc *s = g_malloc0(sizeof(*s)); uint64_t ram_size; QFWCFG *fw_cfg = pc_fw_cfg_init(); + MemBlock *node; + s->opts = flags; s->alloc.alloc = pc_alloc; s->alloc.free = pc_free; @@ -70,5 +319,16 @@ QGuestAllocator *pc_alloc_init(void) /* clean-up */ g_free(fw_cfg); + QTAILQ_INIT(&s->used); + QTAILQ_INIT(&s->free); + + node = mlist_new(s->start, s->end - s->start); + QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME); + return &s->alloc; } + +inline QGuestAllocator *pc_alloc_init(void) +{ + return pc_alloc_init_flags(PC_ALLOC_NO_FLAGS); +} diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h index ff964abe53..9f525e3b99 100644 --- a/tests/libqos/malloc-pc.h +++ b/tests/libqos/malloc-pc.h @@ -15,6 +15,15 @@ #include "libqos/malloc.h" +typedef enum { + PC_ALLOC_NO_FLAGS = 0x00, + PC_ALLOC_LEAK_WARN = 0x01, + PC_ALLOC_LEAK_ASSERT = 0x02, + PC_ALLOC_PARANOID = 0x04 +} PCAllocOpts; + QGuestAllocator *pc_alloc_init(void); +QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags); +void pc_alloc_uninit(QGuestAllocator *allocator); #endif diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index ce0b308a83..d5ce683d77 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -15,8 +15,6 @@ #include "hw/pci/pci_regs.h" #include <glib.h> -#include <stdio.h> - void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), void *data) @@ -75,6 +73,115 @@ void qpci_device_enable(QPCIDevice *dev) qpci_config_writew(dev, PCI_COMMAND, cmd); } +uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id) +{ + uint8_t cap; + uint8_t addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST); + + do { + cap = qpci_config_readb(dev, addr); + if (cap != id) { + addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT); + } + } while (cap != id && addr != 0); + + return addr; +} + +void qpci_msix_enable(QPCIDevice *dev) +{ + uint8_t addr; + uint16_t val; + uint32_t table; + uint8_t bir_table; + uint8_t bir_pba; + void *offset; + + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + g_assert_cmphex(addr, !=, 0); + + val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); + qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE); + + table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE); + bir_table = table & PCI_MSIX_FLAGS_BIRMASK; + offset = qpci_iomap(dev, bir_table, NULL); + dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + + table = qpci_config_readl(dev, addr + PCI_MSIX_PBA); + bir_pba = table & PCI_MSIX_FLAGS_BIRMASK; + if (bir_pba != bir_table) { + offset = qpci_iomap(dev, bir_pba, NULL); + } + dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + + g_assert(dev->msix_table != NULL); + g_assert(dev->msix_pba != NULL); + dev->msix_enabled = true; +} + +void qpci_msix_disable(QPCIDevice *dev) +{ + uint8_t addr; + uint16_t val; + + g_assert(dev->msix_enabled); + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + g_assert_cmphex(addr, !=, 0); + val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); + qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, + val & ~PCI_MSIX_FLAGS_ENABLE); + + qpci_iounmap(dev, dev->msix_table); + qpci_iounmap(dev, dev->msix_pba); + dev->msix_enabled = 0; + dev->msix_table = NULL; + dev->msix_pba = NULL; +} + +bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) +{ + uint32_t pba_entry; + uint8_t bit_n = entry % 32; + void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; + + g_assert(dev->msix_enabled); + pba_entry = qpci_io_readl(dev, addr); + qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n)); + return (pba_entry & (1 << bit_n)) != 0; +} + +bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) +{ + uint8_t addr; + uint16_t val; + void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE); + + g_assert(dev->msix_enabled); + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + g_assert_cmphex(addr, !=, 0); + val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); + + if (val & PCI_MSIX_FLAGS_MASKALL) { + return true; + } else { + return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL) + & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; + } +} + +uint16_t qpci_msix_table_size(QPCIDevice *dev) +{ + uint8_t addr; + uint16_t control; + + addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); + g_assert_cmphex(addr, !=, 0); + + control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); + return (control & PCI_MSIX_FLAGS_QSIZE) + 1; +} + uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset) { return dev->bus->config_readb(dev->bus, dev->devfn, offset); diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index 9ee048b154..d51eb9e219 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -14,6 +14,7 @@ #define LIBQOS_PCI_H #include <stdint.h> +#include "libqtest.h" #define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn)) @@ -49,6 +50,9 @@ struct QPCIDevice { QPCIBus *bus; int devfn; + bool msix_enabled; + void *msix_table; + void *msix_pba; }; void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, @@ -57,6 +61,12 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn); void qpci_device_enable(QPCIDevice *dev); +uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id); +void qpci_msix_enable(QPCIDevice *dev); +void qpci_msix_disable(QPCIDevice *dev); +bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry); +bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry); +uint16_t qpci_msix_table_size(QPCIDevice *dev); uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset); uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset); diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c new file mode 100644 index 0000000000..788ebaff46 --- /dev/null +++ b/tests/libqos/virtio-pci.c @@ -0,0 +1,343 @@ +/* + * libqos virtio PCI driver + * + * Copyright (c) 2014 Marc MarĂ + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdio.h> +#include "libqtest.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc.h" +#include "libqos/malloc-pc.h" + +#include "hw/pci/pci_regs.h" + +typedef struct QVirtioPCIForeachData { + void (*func)(QVirtioDevice *d, void *data); + uint16_t device_type; + void *user_data; +} QVirtioPCIForeachData; + +static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev) +{ + QVirtioPCIDevice *vpcidev; + vpcidev = g_malloc0(sizeof(*vpcidev)); + + if (pdev) { + vpcidev->pdev = pdev; + vpcidev->vdev.device_type = + qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID); + } + + vpcidev->config_msix_entry = -1; + + return vpcidev; +} + +static void qvirtio_pci_foreach_callback( + QPCIDevice *dev, int devfn, void *data) +{ + QVirtioPCIForeachData *d = data; + QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev); + + if (vpcidev->vdev.device_type == d->device_type) { + d->func(&vpcidev->vdev, d->user_data); + } else { + g_free(vpcidev); + } +} + +static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) +{ + QVirtioPCIDevice **vpcidev = data; + *vpcidev = (QVirtioPCIDevice *)d; +} + +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, void *addr) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readb(dev->pdev, addr); +} + +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, void *addr) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readw(dev->pdev, addr); +} + +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, void *addr) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readl(dev->pdev, addr); +} + +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + int i; + uint64_t u64 = 0; + + if (qtest_big_endian()) { + for (i = 0; i < 8; ++i) { + u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << (7 - i) * 8; + } + } else { + for (i = 0; i < 8; ++i) { + u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << i * 8; + } + } + + return u64; +} + +static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_DEVICE_FEATURES); +} + +static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES, features); +} + +static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES); +} + +static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS); +} + +static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS, status); +} + +static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq; + uint32_t data; + + if (dev->pdev->msix_enabled) { + g_assert_cmpint(vqpci->msix_entry, !=, -1); + if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) { + /* No ISR checking should be done if masked, but read anyway */ + return qpci_msix_pending(dev->pdev, vqpci->msix_entry); + } else { + data = readl(vqpci->msix_addr); + writel(vqpci->msix_addr, 0); + return data == vqpci->msix_data; + } + } else { + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 1; + } +} + +static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + uint32_t data; + + if (dev->pdev->msix_enabled) { + g_assert_cmpint(dev->config_msix_entry, !=, -1); + if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) { + /* No ISR checking should be done if masked, but read anyway */ + return qpci_msix_pending(dev->pdev, dev->config_msix_entry); + } else { + data = readl(dev->config_msix_addr); + writel(dev->config_msix_addr, 0); + return data == dev->config_msix_data; + } + } else { + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 2; + } +} + +static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_QUEUE_SELECT, index); +} + +static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_QUEUE_SIZE); +} + +static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_QUEUE_ADDRESS, pfn); +} + +static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) +{ + uint32_t feat; + uint64_t addr; + QVirtQueuePCI *vqpci; + + vqpci = g_malloc0(sizeof(*vqpci)); + feat = qvirtio_pci_get_guest_features(d); + + qvirtio_pci_queue_select(d, index); + vqpci->vq.index = index; + vqpci->vq.size = qvirtio_pci_get_queue_size(d); + vqpci->vq.free_head = 0; + vqpci->vq.num_free = vqpci->vq.size; + vqpci->vq.align = QVIRTIO_PCI_ALIGN; + vqpci->vq.indirect = (feat & QVIRTIO_F_RING_INDIRECT_DESC) != 0; + vqpci->vq.event = (feat & QVIRTIO_F_RING_EVENT_IDX) != 0; + + vqpci->msix_entry = -1; + vqpci->msix_addr = 0; + vqpci->msix_data = 0x12345678; + + /* Check different than 0 */ + g_assert_cmpint(vqpci->vq.size, !=, 0); + + /* Check power of 2 */ + g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0); + + addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, QVIRTIO_PCI_ALIGN)); + qvring_init(alloc, &vqpci->vq, addr); + qvirtio_pci_set_queue_address(d, vqpci->vq.desc / QVIRTIO_PCI_ALIGN); + + return &vqpci->vq; +} + +static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_QUEUE_NOTIFY, vq->index); +} + +const QVirtioBus qvirtio_pci = { + .config_readb = qvirtio_pci_config_readb, + .config_readw = qvirtio_pci_config_readw, + .config_readl = qvirtio_pci_config_readl, + .config_readq = qvirtio_pci_config_readq, + .get_features = qvirtio_pci_get_features, + .set_features = qvirtio_pci_set_features, + .get_guest_features = qvirtio_pci_get_guest_features, + .get_status = qvirtio_pci_get_status, + .set_status = qvirtio_pci_set_status, + .get_queue_isr_status = qvirtio_pci_get_queue_isr_status, + .get_config_isr_status = qvirtio_pci_get_config_isr_status, + .queue_select = qvirtio_pci_queue_select, + .get_queue_size = qvirtio_pci_get_queue_size, + .set_queue_address = qvirtio_pci_set_queue_address, + .virtqueue_setup = qvirtio_pci_virtqueue_setup, + .virtqueue_kick = qvirtio_pci_virtqueue_kick, +}; + +void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, + void (*func)(QVirtioDevice *d, void *data), void *data) +{ + QVirtioPCIForeachData d = { .func = func, + .device_type = device_type, + .user_data = data }; + + qpci_device_foreach(bus, QVIRTIO_VENDOR_ID, -1, + qvirtio_pci_foreach_callback, &d); +} + +QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) +{ + QVirtioPCIDevice *dev = NULL; + qvirtio_pci_foreach(bus, device_type, qvirtio_pci_assign_device, &dev); + + return dev; +} + +void qvirtio_pci_device_enable(QVirtioPCIDevice *d) +{ + qpci_device_enable(d->pdev); + d->addr = qpci_iomap(d->pdev, 0, NULL); + g_assert(d->addr != NULL); +} + +void qvirtio_pci_device_disable(QVirtioPCIDevice *d) +{ + qpci_iounmap(d->pdev, d->addr); + d->addr = NULL; +} + +void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, + QGuestAllocator *alloc, uint16_t entry) +{ + uint16_t vector; + uint32_t control; + void *addr; + + g_assert(d->pdev->msix_enabled); + addr = d->pdev->msix_table + (entry * 16); + + g_assert_cmpint(entry, >=, 0); + g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); + vqpci->msix_entry = entry; + + vqpci->msix_addr = guest_alloc(alloc, 4); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, + vqpci->msix_addr & ~0UL); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, + (vqpci->msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); + + control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + + qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR); + g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); +} + +void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, + QGuestAllocator *alloc, uint16_t entry) +{ + uint16_t vector; + uint32_t control; + void *addr; + + g_assert(d->pdev->msix_enabled); + addr = d->pdev->msix_table + (entry * 16); + + g_assert_cmpint(entry, >=, 0); + g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); + d->config_msix_entry = entry; + + d->config_msix_data = 0x12345678; + d->config_msix_addr = guest_alloc(alloc, 4); + + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, + d->config_msix_addr & ~0UL); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, + (d->config_msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, d->config_msix_data); + + control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + + qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR); + g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); +} diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h new file mode 100644 index 0000000000..883f7ff267 --- /dev/null +++ b/tests/libqos/virtio-pci.h @@ -0,0 +1,61 @@ +/* + * libqos virtio PCI definitions + * + * Copyright (c) 2014 Marc MarĂ + * + * 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 LIBQOS_VIRTIO_PCI_H +#define LIBQOS_VIRTIO_PCI_H + +#include "libqos/virtio.h" +#include "libqos/pci.h" + +#define QVIRTIO_DEVICE_FEATURES 0x00 +#define QVIRTIO_GUEST_FEATURES 0x04 +#define QVIRTIO_QUEUE_ADDRESS 0x08 +#define QVIRTIO_QUEUE_SIZE 0x0C +#define QVIRTIO_QUEUE_SELECT 0x0E +#define QVIRTIO_QUEUE_NOTIFY 0x10 +#define QVIRTIO_DEVICE_STATUS 0x12 +#define QVIRTIO_ISR_STATUS 0x13 +#define QVIRTIO_MSIX_CONF_VECTOR 0x14 +#define QVIRTIO_MSIX_QUEUE_VECTOR 0x16 +#define QVIRTIO_DEVICE_SPECIFIC_MSIX 0x18 +#define QVIRTIO_DEVICE_SPECIFIC_NO_MSIX 0x14 + +#define QVIRTIO_PCI_ALIGN 4096 + +#define QVIRTIO_MSI_NO_VECTOR 0xFFFF + +typedef struct QVirtioPCIDevice { + QVirtioDevice vdev; + QPCIDevice *pdev; + void *addr; + uint16_t config_msix_entry; + uint64_t config_msix_addr; + uint32_t config_msix_data; +} QVirtioPCIDevice; + +typedef struct QVirtQueuePCI { + QVirtQueue vq; + uint16_t msix_entry; + uint64_t msix_addr; + uint32_t msix_data; +} QVirtQueuePCI; + +extern const QVirtioBus qvirtio_pci; + +void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, + void (*func)(QVirtioDevice *d, void *data), void *data); +QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type); +void qvirtio_pci_device_enable(QVirtioPCIDevice *d); +void qvirtio_pci_device_disable(QVirtioPCIDevice *d); + +void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, + QGuestAllocator *alloc, uint16_t entry); +void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, + QGuestAllocator *alloc, uint16_t entry); +#endif diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c new file mode 100644 index 0000000000..128dbd0e9a --- /dev/null +++ b/tests/libqos/virtio.c @@ -0,0 +1,257 @@ +/* + * libqos virtio driver + * + * Copyright (c) 2014 Marc MarĂ + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include "libqtest.h" +#include "libqos/virtio.h" + +uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, + void *addr) +{ + return bus->config_readb(d, addr); +} + +uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, + void *addr) +{ + return bus->config_readw(d, addr); +} + +uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, + void *addr) +{ + return bus->config_readl(d, addr); +} + +uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, + void *addr) +{ + return bus->config_readq(d, addr); +} + +uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d) +{ + return bus->get_features(d); +} + +void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, + uint32_t features) +{ + bus->set_features(d, features); +} + +QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) +{ + return bus->virtqueue_setup(d, alloc, index); +} + +void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d) +{ + bus->set_status(d, QVIRTIO_RESET); + g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_RESET); +} + +void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d) +{ + bus->set_status(d, bus->get_status(d) | QVIRTIO_ACKNOWLEDGE); + g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_ACKNOWLEDGE); +} + +void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d) +{ + bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER); + g_assert_cmphex(bus->get_status(d), ==, + QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE); +} + +void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d) +{ + bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER_OK); + g_assert_cmphex(bus->get_status(d), ==, + QVIRTIO_DRIVER_OK | QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE); +} + +bool qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, + QVirtQueue *vq, uint64_t timeout) +{ + do { + clock_step(10); + if (bus->get_queue_isr_status(d, vq)) { + break; /* It has ended */ + } + } while (--timeout); + + return timeout != 0; +} + +bool qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, + uint64_t timeout) +{ + do { + clock_step(10); + if (bus->get_config_isr_status(d)) { + break; /* It has ended */ + } + } while (--timeout); + + return timeout != 0; +} + +void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr) +{ + int i; + + vq->desc = addr; + vq->avail = vq->desc + vq->size*sizeof(QVRingDesc); + vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size) + + vq->align - 1) & ~(vq->align - 1)); + + for (i = 0; i < vq->size - 1; i++) { + /* vq->desc[i].addr */ + writew(vq->desc + (16 * i), 0); + /* vq->desc[i].next */ + writew(vq->desc + (16 * i) + 14, i + 1); + } + + /* vq->avail->flags */ + writew(vq->avail, 0); + /* vq->avail->idx */ + writew(vq->avail + 2, 0); + /* vq->avail->used_event */ + writew(vq->avail + 4 + (2 * vq->size), 0); + + /* vq->used->flags */ + writew(vq->used, 0); + /* vq->used->avail_event */ + writew(vq->used+2+(sizeof(struct QVRingUsedElem)*vq->size), 0); +} + +QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t elem) +{ + int i; + QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect)); + + indirect->index = 0; + indirect->elem = elem; + indirect->desc = guest_alloc(alloc, sizeof(QVRingDesc)*elem); + + for (i = 0; i < elem - 1; ++i) { + /* indirect->desc[i].addr */ + writeq(indirect->desc + (16 * i), 0); + /* indirect->desc[i].flags */ + writew(indirect->desc + (16 * i) + 12, QVRING_DESC_F_NEXT); + /* indirect->desc[i].next */ + writew(indirect->desc + (16 * i) + 14, i + 1); + } + + return indirect; +} + +void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data, + uint32_t len, bool write) +{ + uint16_t flags; + + g_assert_cmpint(indirect->index, <, indirect->elem); + + flags = readw(indirect->desc + (16 * indirect->index) + 12); + + if (write) { + flags |= QVRING_DESC_F_WRITE; + } + + /* indirect->desc[indirect->index].addr */ + writeq(indirect->desc + (16 * indirect->index), data); + /* indirect->desc[indirect->index].len */ + writel(indirect->desc + (16 * indirect->index) + 8, len); + /* indirect->desc[indirect->index].flags */ + writew(indirect->desc + (16 * indirect->index) + 12, flags); + + indirect->index++; +} + +uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write, + bool next) +{ + uint16_t flags = 0; + vq->num_free--; + + if (write) { + flags |= QVRING_DESC_F_WRITE; + } + + if (next) { + flags |= QVRING_DESC_F_NEXT; + } + + /* vq->desc[vq->free_head].addr */ + writeq(vq->desc + (16 * vq->free_head), data); + /* vq->desc[vq->free_head].len */ + writel(vq->desc + (16 * vq->free_head) + 8, len); + /* vq->desc[vq->free_head].flags */ + writew(vq->desc + (16 * vq->free_head) + 12, flags); + + return vq->free_head++; /* Return and increase, in this order */ +} + +uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect) +{ + g_assert(vq->indirect); + g_assert_cmpint(vq->size, >=, indirect->elem); + g_assert_cmpint(indirect->index, ==, indirect->elem); + + vq->num_free--; + + /* vq->desc[vq->free_head].addr */ + writeq(vq->desc + (16 * vq->free_head), indirect->desc); + /* vq->desc[vq->free_head].len */ + writel(vq->desc + (16 * vq->free_head) + 8, + sizeof(QVRingDesc) * indirect->elem); + /* vq->desc[vq->free_head].flags */ + writew(vq->desc + (16 * vq->free_head) + 12, QVRING_DESC_F_INDIRECT); + + return vq->free_head++; /* Return and increase, in this order */ +} + +void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, + uint32_t free_head) +{ + /* vq->avail->idx */ + uint16_t idx = readl(vq->avail + 2); + /* vq->used->flags */ + uint16_t flags; + /* vq->used->avail_event */ + uint16_t avail_event; + + /* vq->avail->ring[idx % vq->size] */ + writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head); + /* vq->avail->idx */ + writel(vq->avail + 2, idx + 1); + + /* Must read after idx is updated */ + flags = readw(vq->avail); + avail_event = readw(vq->used + 4 + + (sizeof(struct QVRingUsedElem) * vq->size)); + + /* < 1 because we add elements to avail queue one by one */ + if ((flags & QVRING_USED_F_NO_NOTIFY) == 0 && + (!vq->event || (uint16_t)(idx-avail_event) < 1)) { + bus->virtqueue_kick(d, vq); + } +} + +void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx) +{ + g_assert(vq->event); + + /* vq->avail->used_event */ + writew(vq->avail + 4 + (2 * vq->size), idx); +} diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h new file mode 100644 index 0000000000..70b3376360 --- /dev/null +++ b/tests/libqos/virtio.h @@ -0,0 +1,182 @@ +/* + * libqos virtio definitions + * + * Copyright (c) 2014 Marc MarĂ + * + * 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 LIBQOS_VIRTIO_H +#define LIBQOS_VIRTIO_H + +#include "libqos/malloc.h" + +#define QVIRTIO_VENDOR_ID 0x1AF4 + +#define QVIRTIO_RESET 0x0 +#define QVIRTIO_ACKNOWLEDGE 0x1 +#define QVIRTIO_DRIVER 0x2 +#define QVIRTIO_DRIVER_OK 0x4 + +#define QVIRTIO_NET_DEVICE_ID 0x1 +#define QVIRTIO_BLK_DEVICE_ID 0x2 + +#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000 +#define QVIRTIO_F_ANY_LAYOUT 0x08000000 +#define QVIRTIO_F_RING_INDIRECT_DESC 0x10000000 +#define QVIRTIO_F_RING_EVENT_IDX 0x20000000 +#define QVIRTIO_F_BAD_FEATURE 0x40000000 + +#define QVRING_DESC_F_NEXT 0x1 +#define QVRING_DESC_F_WRITE 0x2 +#define QVRING_DESC_F_INDIRECT 0x4 + +#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000 +#define QVIRTIO_F_ANY_LAYOUT 0x08000000 +#define QVIRTIO_F_RING_INDIRECT_DESC 0x10000000 +#define QVIRTIO_F_RING_EVENT_IDX 0x20000000 +#define QVIRTIO_F_BAD_FEATURE 0x40000000 + +#define QVRING_AVAIL_F_NO_INTERRUPT 1 + +#define QVRING_USED_F_NO_NOTIFY 1 + +typedef struct QVirtioDevice { + /* Device type */ + uint16_t device_type; +} QVirtioDevice; + +typedef struct QVRingDesc { + uint64_t addr; + uint32_t len; + uint16_t flags; + uint16_t next; +} QVRingDesc; + +typedef struct QVRingAvail { + uint16_t flags; + uint16_t idx; + uint16_t ring[0]; /* This is an array of uint16_t */ + uint16_t used_event; +} QVRingAvail; + +typedef struct QVRingUsedElem { + uint32_t id; + uint32_t len; +} QVRingUsedElem; + +typedef struct QVRingUsed { + uint16_t flags; + uint16_t idx; + QVRingUsedElem ring[0]; /* This is an array of QVRingUsedElem structs */ + uint16_t avail_event; +} QVRingUsed; + +typedef struct QVirtQueue { + uint64_t desc; /* This points to an array of QVRingDesc */ + uint64_t avail; /* This points to a QVRingAvail */ + uint64_t used; /* This points to a QVRingDesc */ + uint16_t index; + uint32_t size; + uint32_t free_head; + uint32_t num_free; + uint32_t align; + bool indirect; + bool event; +} QVirtQueue; + +typedef struct QVRingIndirectDesc { + uint64_t desc; /* This points to an array fo QVRingDesc */ + uint16_t index; + uint16_t elem; +} QVRingIndirectDesc; + +typedef struct QVirtioBus { + uint8_t (*config_readb)(QVirtioDevice *d, void *addr); + uint16_t (*config_readw)(QVirtioDevice *d, void *addr); + uint32_t (*config_readl)(QVirtioDevice *d, void *addr); + uint64_t (*config_readq)(QVirtioDevice *d, void *addr); + + /* Get features of the device */ + uint32_t (*get_features)(QVirtioDevice *d); + + /* Set features of the device */ + void (*set_features)(QVirtioDevice *d, uint32_t features); + + /* Get features of the guest */ + uint32_t (*get_guest_features)(QVirtioDevice *d); + + /* Get status of the device */ + uint8_t (*get_status)(QVirtioDevice *d); + + /* Set status of the device */ + void (*set_status)(QVirtioDevice *d, uint8_t status); + + /* Get the queue ISR status of the device */ + bool (*get_queue_isr_status)(QVirtioDevice *d, QVirtQueue *vq); + + /* Get the configuration ISR status of the device */ + bool (*get_config_isr_status)(QVirtioDevice *d); + + /* Select a queue to work on */ + void (*queue_select)(QVirtioDevice *d, uint16_t index); + + /* Get the size of the selected queue */ + uint16_t (*get_queue_size)(QVirtioDevice *d); + + /* Set the address of the selected queue */ + void (*set_queue_address)(QVirtioDevice *d, uint32_t pfn); + + /* Setup the virtqueue specified by index */ + QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc, + uint16_t index); + + /* Notify changes in virtqueue */ + void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq); +} QVirtioBus; + +static inline uint32_t qvring_size(uint32_t num, uint32_t align) +{ + return ((sizeof(struct QVRingDesc) * num + sizeof(uint16_t) * (3 + num) + + align - 1) & ~(align - 1)) + + sizeof(uint16_t) * 3 + sizeof(struct QVRingUsedElem) * num; +} + +uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, + void *addr); +uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, + void *addr); +uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, + void *addr); +uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, + void *addr); +uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d); +void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, + uint32_t features); + +void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d); +void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d); +void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d); +void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d); + +bool qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, + QVirtQueue *vq, uint64_t timeout); +bool qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, + uint64_t timeout); +QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index); + +void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr); +QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t elem); +void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data, + uint32_t len, bool write); +uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write, + bool next); +uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect); +void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, + uint32_t free_head); + +void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); +#endif diff --git a/tests/libqtest.c b/tests/libqtest.c index 5e458e884e..9a92aa70e4 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -696,3 +696,51 @@ void qmp_discard_response(const char *fmt, ...) qtest_qmpv_discard_response(global_qtest, fmt, ap); va_end(ap); } + +bool qtest_big_endian(void) +{ + const char *arch = qtest_get_arch(); + int i; + + static const struct { + const char *arch; + bool big_endian; + } endianness[] = { + { "aarch64", false }, + { "alpha", false }, + { "arm", false }, + { "cris", false }, + { "i386", false }, + { "lm32", true }, + { "m68k", true }, + { "microblaze", true }, + { "microblazeel", false }, + { "mips", true }, + { "mips64", true }, + { "mips64el", false }, + { "mipsel", false }, + { "moxie", true }, + { "or32", true }, + { "ppc", true }, + { "ppc64", true }, + { "ppcemb", true }, + { "s390x", true }, + { "sh4", false }, + { "sh4eb", true }, + { "sparc", true }, + { "sparc64", true }, + { "unicore32", false }, + { "x86_64", false }, + { "xtensa", false }, + { "xtensaeb", true }, + {}, + }; + + for (i = 0; endianness[i].arch; i++) { + if (strcmp(endianness[i].arch, arch) == 0) { + return endianness[i].big_endian; + } + } + + return false; +} diff --git a/tests/libqtest.h b/tests/libqtest.h index 1be0934f07..3e12cab2f2 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -682,4 +682,11 @@ static inline int64_t clock_set(int64_t val) return qtest_clock_set(global_qtest, val); } +/** + * qtest_big_endian: + * + * Returns: True if the architecture under test has a big endian configuration. + */ +bool qtest_big_endian(void); + #endif diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index d53f875b89..588666cff1 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -2,6 +2,7 @@ * QTest testcase for VirtIO Block Device * * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2014 Marc MarĂ * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -9,12 +10,634 @@ #include <glib.h> #include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> #include "libqtest.h" -#include "qemu/osdep.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc.h" +#include "libqos/malloc-pc.h" +#include "qemu/bswap.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +#define QVIRTIO_BLK_F_BARRIER 0x00000001 +#define QVIRTIO_BLK_F_SIZE_MAX 0x00000002 +#define QVIRTIO_BLK_F_SEG_MAX 0x00000004 +#define QVIRTIO_BLK_F_GEOMETRY 0x00000010 +#define QVIRTIO_BLK_F_RO 0x00000020 +#define QVIRTIO_BLK_F_BLK_SIZE 0x00000040 +#define QVIRTIO_BLK_F_SCSI 0x00000080 +#define QVIRTIO_BLK_F_WCE 0x00000200 +#define QVIRTIO_BLK_F_TOPOLOGY 0x00000400 +#define QVIRTIO_BLK_F_CONFIG_WCE 0x00000800 + +#define QVIRTIO_BLK_T_IN 0 +#define QVIRTIO_BLK_T_OUT 1 +#define QVIRTIO_BLK_T_SCSI_CMD 2 +#define QVIRTIO_BLK_T_SCSI_CMD_OUT 3 +#define QVIRTIO_BLK_T_FLUSH 4 +#define QVIRTIO_BLK_T_FLUSH_OUT 5 +#define QVIRTIO_BLK_T_GET_ID 8 + +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +#define QVIRTIO_BLK_TIMEOUT 100 +#define PCI_SLOT 0x04 +#define PCI_FN 0x00 + +typedef struct QVirtioBlkReq { + uint32_t type; + uint32_t ioprio; + uint64_t sector; + char *data; + uint8_t status; +} QVirtioBlkReq; + +static QPCIBus *test_start(void) +{ + char cmdline[100]; + char tmp_path[] = "/tmp/qtest.XXXXXX"; + int fd, ret; + + /* Create a temporary raw image */ + fd = mkstemp(tmp_path); + g_assert_cmpint(fd, >=, 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert_cmpint(ret, ==, 0); + close(fd); + + snprintf(cmdline, 100, "-drive if=none,id=drive0,file=%s " + "-device virtio-blk-pci,drive=drive0,addr=%x.%x", + tmp_path, PCI_SLOT, PCI_FN); + qtest_start(cmdline); + unlink(tmp_path); + + return qpci_init_pc(); +} + +static void test_end(void) +{ + qtest_end(); +} + +static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus) +{ + QVirtioPCIDevice *dev; + + dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID); + g_assert(dev != NULL); + g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID); + g_assert_cmphex(dev->pdev->devfn, ==, ((PCI_SLOT << 3) | PCI_FN)); + + qvirtio_pci_device_enable(dev); + qvirtio_reset(&qvirtio_pci, &dev->vdev); + qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver(&qvirtio_pci, &dev->vdev); + + return dev; +} + +static inline void virtio_blk_fix_request(QVirtioBlkReq *req) +{ +#ifdef HOST_WORDS_BIGENDIAN + bool host_endian = true; +#else + bool host_endian = false; +#endif + + if (qtest_big_endian() != host_endian) { + req->type = bswap32(req->type); + req->ioprio = bswap32(req->ioprio); + req->sector = bswap64(req->sector); + } +} + +static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, + uint64_t data_size) +{ + uint64_t addr; + uint8_t status = 0xFF; + + g_assert_cmpuint(data_size % 512, ==, 0); + addr = guest_alloc(alloc, sizeof(*req) + data_size); + + virtio_blk_fix_request(req); + + memwrite(addr, req, 16); + memwrite(addr + 16, req->data, data_size); + memwrite(addr + 16 + data_size, &status, sizeof(status)); + + return addr; +} + +static void pci_basic(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + QVirtioBlkReq req; + void *addr; + uint64_t req_addr; + uint64_t capacity; + uint32_t features; + uint32_t free_head; + uint8_t status; + char *data; + + bus = test_start(); + + dev = virtio_blk_init(bus); + + /* MSI-X is not enabled */ + addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX | + QVIRTIO_BLK_F_SCSI); + qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + + alloc = pc_alloc_init(); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + + qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + + /* Write and read with 2 descriptor layout */ + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + + /* Write and read with 3 descriptor layout */ + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + + /* End test */ + guest_free(alloc, vqpci->vq.desc); + qvirtio_pci_device_disable(dev); + g_free(dev); + test_end(); +} + +static void pci_indirect(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + QVirtioBlkReq req; + QVRingIndirectDesc *indirect; + void *addr; + uint64_t req_addr; + uint64_t capacity; + uint32_t features; + uint32_t free_head; + uint8_t status; + char *data; + + bus = test_start(); + + dev = virtio_blk_init(bus); + + /* MSI-X is not enabled */ + addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + g_assert_cmphex(features & QVIRTIO_F_RING_INDIRECT_DESC, !=, 0); + features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_EVENT_IDX | + QVIRTIO_BLK_F_SCSI); + qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + + alloc = pc_alloc_init(); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2); + qvring_indirect_desc_add(indirect, req_addr, 528, false); + qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); + free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + g_free(indirect); + guest_free(alloc, req_addr); + + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2); + qvring_indirect_desc_add(indirect, req_addr, 16, false); + qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); + free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + g_free(indirect); + guest_free(alloc, req_addr); + + /* End test */ + guest_free(alloc, vqpci->vq.desc); + qvirtio_pci_device_disable(dev); + g_free(dev); + test_end(); +} + +static void pci_config(void) { + QVirtioPCIDevice *dev; + QPCIBus *bus; + int n_size = TEST_IMAGE_SIZE / 2; + void *addr; + uint64_t capacity; + + bus = test_start(); + + dev = virtio_blk_init(bus); + + /* MSI-X is not enabled */ + addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + + qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + g_assert(qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, + QVIRTIO_BLK_TIMEOUT)); + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, n_size / 512); + + qvirtio_pci_device_disable(dev); + g_free(dev); + test_end(); +} + +static void pci_msix(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + QVirtioBlkReq req; + int n_size = TEST_IMAGE_SIZE / 2; + void *addr; + uint64_t req_addr; + uint64_t capacity; + uint32_t features; + uint32_t free_head; + uint8_t status; + char *data; + + bus = test_start(); + alloc = pc_alloc_init(); + + dev = virtio_blk_init(bus); + qpci_msix_enable(dev->pdev); + + qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); + + /* MSI-X is enabled */ + addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + QVIRTIO_F_RING_INDIRECT_DESC | + QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI); + qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1); + + qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + + qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + + g_assert(qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, + QVIRTIO_BLK_TIMEOUT)); + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, n_size / 512); + + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + + /* End test */ + guest_free(alloc, (uint64_t)vqpci->vq.desc); + qpci_msix_disable(dev->pdev); + qvirtio_pci_device_disable(dev); + g_free(dev); + test_end(); +} + +static void pci_idx(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + QVirtioBlkReq req; + void *addr; + uint64_t req_addr; + uint64_t capacity; + uint32_t features; + uint32_t free_head; + uint8_t status; + char *data; + + bus = test_start(); + alloc = pc_alloc_init(); + + dev = virtio_blk_init(bus); + qpci_msix_enable(dev->pdev); + + qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); + + /* MSI-X is enabled */ + addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + QVIRTIO_F_RING_INDIRECT_DESC | + QVIRTIO_F_NOTIFY_ON_EMPTY | QVIRTIO_BLK_F_SCSI); + qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1); + + qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + /* Notify after processing the third request */ + qvirtqueue_set_used_event(&vqpci->vq, 2); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + /* No notification expected */ + g_assert(!qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + + qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + + + g_assert(qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + QVIRTIO_BLK_TIMEOUT)); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + + /* End test */ + guest_free(alloc, vqpci->vq.desc); + qpci_msix_disable(dev->pdev); + qvirtio_pci_device_disable(dev); + g_free(dev); + test_end(); } int main(int argc, char **argv) @@ -22,13 +645,14 @@ int main(int argc, char **argv) int ret; g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/blk/pci/nop", pci_nop); - qtest_start("-drive id=drv0,if=none,file=/dev/null " - "-device virtio-blk-pci,drive=drv0"); - ret = g_test_run(); + g_test_add_func("/virtio/blk/pci/basic", pci_basic); + g_test_add_func("/virtio/blk/pci/indirect", pci_indirect); + g_test_add_func("/virtio/blk/pci/config", pci_config); + g_test_add_func("/virtio/blk/pci/msix", pci_msix); + g_test_add_func("/virtio/blk/pci/idx", pci_idx); - qtest_end(); + ret = g_test_run(); return ret; } diff --git a/ui/spice-display.c b/ui/spice-display.c index 66e25788ce..def7b52e9c 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -334,11 +334,23 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) { QXLDevSurfaceCreate surface; + uint64_t surface_size; memset(&surface, 0, sizeof(surface)); - dprint(1, "%s/%d: %dx%d\n", __func__, ssd->qxl.id, - surface_width(ssd->ds), surface_height(ssd->ds)); + surface_size = (uint64_t) surface_width(ssd->ds) * + surface_height(ssd->ds) * 4; + assert(surface_size > 0); + assert(surface_size < INT_MAX); + if (ssd->bufsize < surface_size) { + ssd->bufsize = surface_size; + g_free(ssd->buf); + ssd->buf = g_malloc(ssd->bufsize); + } + + dprint(1, "%s/%d: %ux%u (size %" PRIu64 "/%d)\n", __func__, ssd->qxl.id, + surface_width(ssd->ds), surface_height(ssd->ds), + surface_size, ssd->bufsize); surface.format = SPICE_SURFACE_FMT_32_xRGB; surface.width = surface_width(ssd->ds); @@ -369,8 +381,6 @@ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd) if (ssd->num_surfaces == 0) { ssd->num_surfaces = 1024; } - ssd->bufsize = (16 * 1024 * 1024); - ssd->buf = g_malloc(ssd->bufsize); } /* display listener callbacks */ @@ -495,7 +505,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) info->num_memslots = NUM_MEMSLOTS; info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; info->internal_groupslot_id = 0; - info->qxl_ram_size = ssd->bufsize; + info->qxl_ram_size = 16 * 1024 * 1024; info->n_surfaces = ssd->num_surfaces; } |