diff options
282 files changed, 11631 insertions, 4463 deletions
diff --git a/.gitmodules b/.gitmodules index 9da9ede261..ca323b4d87 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "roms/u-boot"] path = roms/u-boot url = git://git.qemu-project.org/u-boot.git +[submodule "roms/skiboot"] + path = roms/skiboot + url = git://git.qemu.org/skiboot.git diff --git a/MAINTAINERS b/MAINTAINERS index b01fec0a7b..3fecf458c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -63,6 +63,17 @@ W: http://wiki.qemu.org/SecurityProcess M: Michael S. Tsirkin <mst@redhat.com> L: secalert@redhat.com +Trivial patches +--------------- +Trivial patches +M: Michael Tokarev <mjt@tls.msk.ru> +M: Laurent Vivier <laurent@vivier.eu> +S: Maintained +L: qemu-trivial@nongnu.org +K: ^Subject:.*(?i)trivial +T: git git://git.corpit.ru/qemu.git trivial-patches +T: git git://github.com/vivier/qemu.git trivial-patches + Guest CPU cores (TCG): ---------------------- Overall @@ -133,7 +144,8 @@ F: include/hw/lm32/ F: tests/tcg/lm32/ M68K -S: Orphan +M: Laurent Vivier <laurent@vivier.eu> +S: Maintained F: target-m68k/ F: hw/m68k/ @@ -656,6 +668,7 @@ F: include/hw/*/xics* F: pc-bios/spapr-rtas/* F: pc-bios/spapr-rtas.bin F: pc-bios/slof.bin +F: pc-bios/skiboot.lid F: docs/specs/ppc-spapr-hcalls.txt F: docs/specs/ppc-spapr-hotplug.txt F: tests/spapr* @@ -1413,6 +1426,14 @@ F: util/uuid.c F: include/qemu/uuid.h F: tests/test-uuid.c +COLO Framework +M: zhanghailiang <zhang.zhanghailiang@huawei.com> +S: Maintained +F: migration/colo* +F: include/migration/colo.h +F: include/migration/failover.h +F: docs/COLO-FT.txt + COLO Proxy M: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> M: Li Zhijian <lizhijian@cn.fujitsu.com> @@ -421,7 +421,7 @@ qemu-icon.bmp qemu_logo_no_text.svg \ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ s390-ccw.img \ -spapr-rtas.bin slof.bin \ +spapr-rtas.bin slof.bin skiboot.lid \ palcode-clipper \ u-boot.e500 else @@ -695,7 +695,7 @@ help: @echo '' ifdef CONFIG_WIN32 @echo 'Windows targets:' - @echo ' installer - Build NSIS-based installer for qemu-ga' + @echo ' installer - Build NSIS-based installer for QEMU' ifdef QEMU_GA_MSI_ENABLED @echo ' msi - Build MSI-based installer for qemu-ga' endif @@ -33,7 +33,6 @@ #include "sysemu/qtest.h" #include "hw/xen/xen.h" #include "qom/object.h" -#include "hw/boards.h" int tcg_tb_size; static bool tcg_allowed = true; @@ -61,6 +61,7 @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) smp_wmb(); ctx->first_bh = bh; qemu_mutex_unlock(&ctx->bh_lock); + aio_notify(ctx); } QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) @@ -106,8 +107,8 @@ int aio_bh_poll(AioContext *ctx) * aio_notify again if necessary. */ if (atomic_xchg(&bh->scheduled, 0)) { - /* Idle BHs and the notify BH don't count as progress */ - if (!bh->idle && bh != ctx->notify_dummy_bh) { + /* Idle BHs don't count as progress */ + if (!bh->idle) { ret = 1; } bh->idle = 0; @@ -259,7 +260,6 @@ aio_ctx_finalize(GSource *source) { AioContext *ctx = (AioContext *) source; - qemu_bh_delete(ctx->notify_dummy_bh); thread_pool_free(ctx->thread_pool); #ifdef CONFIG_LINUX_AIO @@ -284,7 +284,7 @@ aio_ctx_finalize(GSource *source) aio_set_event_notifier(ctx, &ctx->notifier, false, NULL); event_notifier_cleanup(&ctx->notifier); - rfifolock_destroy(&ctx->lock); + qemu_rec_mutex_destroy(&ctx->lock); qemu_mutex_destroy(&ctx->bh_lock); timerlistgroup_deinit(&ctx->tlg); } @@ -345,19 +345,6 @@ static void aio_timerlist_notify(void *opaque) aio_notify(opaque); } -static void aio_rfifolock_cb(void *opaque) -{ - AioContext *ctx = opaque; - - /* Kick owner thread in case they are blocked in aio_poll() */ - qemu_bh_schedule(ctx->notify_dummy_bh); -} - -static void notify_dummy_bh(void *opaque) -{ - /* Do nothing, we were invoked just to force the event loop to iterate */ -} - static void event_notifier_dummy_cb(EventNotifier *e) { } @@ -385,11 +372,9 @@ AioContext *aio_context_new(Error **errp) #endif ctx->thread_pool = NULL; qemu_mutex_init(&ctx->bh_lock); - rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx); + qemu_rec_mutex_init(&ctx->lock); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); - ctx->notify_dummy_bh = aio_bh_new(ctx, notify_dummy_bh, NULL); - return ctx; fail: g_source_destroy(&ctx->source); @@ -408,10 +393,10 @@ void aio_context_unref(AioContext *ctx) void aio_context_acquire(AioContext *ctx) { - rfifolock_lock(&ctx->lock); + qemu_rec_mutex_lock(&ctx->lock); } void aio_context_release(AioContext *ctx) { - rfifolock_unlock(&ctx->lock); + qemu_rec_mutex_unlock(&ctx->lock); } diff --git a/backends/baum.c b/backends/baum.c index 919844e7f0..b92369d840 100644 --- a/backends/baum.c +++ b/backends/baum.c @@ -1,7 +1,7 @@ /* * QEMU Baum Braille Device * - * Copyright (c) 2008 Samuel Thibault + * Copyright (c) 2008, 2010-2011, 2016 Samuel Thibault * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -92,6 +92,7 @@ typedef struct { brlapi_handle_t *brlapi; int brlapi_fd; unsigned int x, y; + bool deferred_init; uint8_t in_buf[BUF_SIZE]; uint8_t in_buf_used; @@ -102,8 +103,11 @@ typedef struct { } BaumDriverState; /* Let's assume NABCC by default */ -static const uint8_t nabcc_translation[256] = { - [0] = ' ', +enum way { + DOTS2ASCII, + ASCII2DOTS +}; +static const uint8_t nabcc_translation[2][256] = { #ifndef BRLAPI_DOTS #define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \ ((d1?BRLAPI_DOT1:0)|\ @@ -115,107 +119,154 @@ static const uint8_t nabcc_translation[256] = { (d7?BRLAPI_DOT7:0)|\ (d8?BRLAPI_DOT8:0)) #endif - [BRLAPI_DOTS(1,0,0,0,0,0,0,0)] = 'a', - [BRLAPI_DOTS(1,1,0,0,0,0,0,0)] = 'b', - [BRLAPI_DOTS(1,0,0,1,0,0,0,0)] = 'c', - [BRLAPI_DOTS(1,0,0,1,1,0,0,0)] = 'd', - [BRLAPI_DOTS(1,0,0,0,1,0,0,0)] = 'e', - [BRLAPI_DOTS(1,1,0,1,0,0,0,0)] = 'f', - [BRLAPI_DOTS(1,1,0,1,1,0,0,0)] = 'g', - [BRLAPI_DOTS(1,1,0,0,1,0,0,0)] = 'h', - [BRLAPI_DOTS(0,1,0,1,0,0,0,0)] = 'i', - [BRLAPI_DOTS(0,1,0,1,1,0,0,0)] = 'j', - [BRLAPI_DOTS(1,0,1,0,0,0,0,0)] = 'k', - [BRLAPI_DOTS(1,1,1,0,0,0,0,0)] = 'l', - [BRLAPI_DOTS(1,0,1,1,0,0,0,0)] = 'm', - [BRLAPI_DOTS(1,0,1,1,1,0,0,0)] = 'n', - [BRLAPI_DOTS(1,0,1,0,1,0,0,0)] = 'o', - [BRLAPI_DOTS(1,1,1,1,0,0,0,0)] = 'p', - [BRLAPI_DOTS(1,1,1,1,1,0,0,0)] = 'q', - [BRLAPI_DOTS(1,1,1,0,1,0,0,0)] = 'r', - [BRLAPI_DOTS(0,1,1,1,0,0,0,0)] = 's', - [BRLAPI_DOTS(0,1,1,1,1,0,0,0)] = 't', - [BRLAPI_DOTS(1,0,1,0,0,1,0,0)] = 'u', - [BRLAPI_DOTS(1,1,1,0,0,1,0,0)] = 'v', - [BRLAPI_DOTS(0,1,0,1,1,1,0,0)] = 'w', - [BRLAPI_DOTS(1,0,1,1,0,1,0,0)] = 'x', - [BRLAPI_DOTS(1,0,1,1,1,1,0,0)] = 'y', - [BRLAPI_DOTS(1,0,1,0,1,1,0,0)] = 'z', - - [BRLAPI_DOTS(1,0,0,0,0,0,1,0)] = 'A', - [BRLAPI_DOTS(1,1,0,0,0,0,1,0)] = 'B', - [BRLAPI_DOTS(1,0,0,1,0,0,1,0)] = 'C', - [BRLAPI_DOTS(1,0,0,1,1,0,1,0)] = 'D', - [BRLAPI_DOTS(1,0,0,0,1,0,1,0)] = 'E', - [BRLAPI_DOTS(1,1,0,1,0,0,1,0)] = 'F', - [BRLAPI_DOTS(1,1,0,1,1,0,1,0)] = 'G', - [BRLAPI_DOTS(1,1,0,0,1,0,1,0)] = 'H', - [BRLAPI_DOTS(0,1,0,1,0,0,1,0)] = 'I', - [BRLAPI_DOTS(0,1,0,1,1,0,1,0)] = 'J', - [BRLAPI_DOTS(1,0,1,0,0,0,1,0)] = 'K', - [BRLAPI_DOTS(1,1,1,0,0,0,1,0)] = 'L', - [BRLAPI_DOTS(1,0,1,1,0,0,1,0)] = 'M', - [BRLAPI_DOTS(1,0,1,1,1,0,1,0)] = 'N', - [BRLAPI_DOTS(1,0,1,0,1,0,1,0)] = 'O', - [BRLAPI_DOTS(1,1,1,1,0,0,1,0)] = 'P', - [BRLAPI_DOTS(1,1,1,1,1,0,1,0)] = 'Q', - [BRLAPI_DOTS(1,1,1,0,1,0,1,0)] = 'R', - [BRLAPI_DOTS(0,1,1,1,0,0,1,0)] = 'S', - [BRLAPI_DOTS(0,1,1,1,1,0,1,0)] = 'T', - [BRLAPI_DOTS(1,0,1,0,0,1,1,0)] = 'U', - [BRLAPI_DOTS(1,1,1,0,0,1,1,0)] = 'V', - [BRLAPI_DOTS(0,1,0,1,1,1,1,0)] = 'W', - [BRLAPI_DOTS(1,0,1,1,0,1,1,0)] = 'X', - [BRLAPI_DOTS(1,0,1,1,1,1,1,0)] = 'Y', - [BRLAPI_DOTS(1,0,1,0,1,1,1,0)] = 'Z', - - [BRLAPI_DOTS(0,0,1,0,1,1,0,0)] = '0', - [BRLAPI_DOTS(0,1,0,0,0,0,0,0)] = '1', - [BRLAPI_DOTS(0,1,1,0,0,0,0,0)] = '2', - [BRLAPI_DOTS(0,1,0,0,1,0,0,0)] = '3', - [BRLAPI_DOTS(0,1,0,0,1,1,0,0)] = '4', - [BRLAPI_DOTS(0,1,0,0,0,1,0,0)] = '5', - [BRLAPI_DOTS(0,1,1,0,1,0,0,0)] = '6', - [BRLAPI_DOTS(0,1,1,0,1,1,0,0)] = '7', - [BRLAPI_DOTS(0,1,1,0,0,1,0,0)] = '8', - [BRLAPI_DOTS(0,0,1,0,1,0,0,0)] = '9', - - [BRLAPI_DOTS(0,0,0,1,0,1,0,0)] = '.', - [BRLAPI_DOTS(0,0,1,1,0,1,0,0)] = '+', - [BRLAPI_DOTS(0,0,1,0,0,1,0,0)] = '-', - [BRLAPI_DOTS(1,0,0,0,0,1,0,0)] = '*', - [BRLAPI_DOTS(0,0,1,1,0,0,0,0)] = '/', - [BRLAPI_DOTS(1,1,1,0,1,1,0,0)] = '(', - [BRLAPI_DOTS(0,1,1,1,1,1,0,0)] = ')', - - [BRLAPI_DOTS(1,1,1,1,0,1,0,0)] = '&', - [BRLAPI_DOTS(0,0,1,1,1,1,0,0)] = '#', - - [BRLAPI_DOTS(0,0,0,0,0,1,0,0)] = ',', - [BRLAPI_DOTS(0,0,0,0,1,1,0,0)] = ';', - [BRLAPI_DOTS(1,0,0,0,1,1,0,0)] = ':', - [BRLAPI_DOTS(0,1,1,1,0,1,0,0)] = '!', - [BRLAPI_DOTS(1,0,0,1,1,1,0,0)] = '?', - [BRLAPI_DOTS(0,0,0,0,1,0,0,0)] = '"', - [BRLAPI_DOTS(0,0,1,0,0,0,0,0)] ='\'', - [BRLAPI_DOTS(0,0,0,1,0,0,0,0)] = '`', - [BRLAPI_DOTS(0,0,0,1,1,0,1,0)] = '^', - [BRLAPI_DOTS(0,0,0,1,1,0,0,0)] = '~', - [BRLAPI_DOTS(0,1,0,1,0,1,1,0)] = '[', - [BRLAPI_DOTS(1,1,0,1,1,1,1,0)] = ']', - [BRLAPI_DOTS(0,1,0,1,0,1,0,0)] = '{', - [BRLAPI_DOTS(1,1,0,1,1,1,0,0)] = '}', - [BRLAPI_DOTS(1,1,1,1,1,1,0,0)] = '=', - [BRLAPI_DOTS(1,1,0,0,0,1,0,0)] = '<', - [BRLAPI_DOTS(0,0,1,1,1,0,0,0)] = '>', - [BRLAPI_DOTS(1,1,0,1,0,1,0,0)] = '$', - [BRLAPI_DOTS(1,0,0,1,0,1,0,0)] = '%', - [BRLAPI_DOTS(0,0,0,1,0,0,1,0)] = '@', - [BRLAPI_DOTS(1,1,0,0,1,1,0,0)] = '|', - [BRLAPI_DOTS(1,1,0,0,1,1,1,0)] ='\\', - [BRLAPI_DOTS(0,0,0,1,1,1,0,0)] = '_', +#define DO(dots, ascii) \ + [DOTS2ASCII][dots] = ascii, \ + [ASCII2DOTS][ascii] = dots + DO(0, ' '), + DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 0, 0), 'a'), + DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 0, 0), 'b'), + DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 0, 0), 'c'), + DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 0, 0), 'd'), + DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 0, 0), 'e'), + DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 0, 0), 'f'), + DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 0, 0), 'g'), + DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 0, 0), 'h'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 0, 0), 'i'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 0, 0), 'j'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 0, 0), 'k'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 0, 0), 'l'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 0, 0), 'm'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 0, 0), 'n'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 0, 0), 'o'), + DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 0, 0), 'p'), + DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 0, 0), 'q'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 0, 0), 'r'), + DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 0, 0), 's'), + DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 0, 0), 't'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 0, 0), 'u'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 0, 0), 'v'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 0, 0), 'w'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 0, 0), 'x'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 0, 0), 'y'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 0, 0), 'z'), + + DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 1, 0), 'A'), + DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 1, 0), 'B'), + DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 1, 0), 'C'), + DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 1, 0), 'D'), + DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 1, 0), 'E'), + DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 1, 0), 'F'), + DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 1, 0), 'G'), + DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 1, 0), 'H'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 1, 0), 'I'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 1, 0), 'J'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 1, 0), 'K'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 1, 0), 'L'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 1, 0), 'M'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 1, 0), 'N'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 1, 0), 'O'), + DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 1, 0), 'P'), + DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 1, 0), 'Q'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 1, 0), 'R'), + DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 1, 0), 'S'), + DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 1, 0), 'T'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 1, 0), 'U'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 1, 0), 'V'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 1, 0), 'W'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 1, 0), 'X'), + DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 1, 0), 'Y'), + DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 1, 0), 'Z'), + + DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 1, 0, 0), '0'), + DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 0, 0, 0), '1'), + DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 0, 0, 0), '2'), + DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 0, 0, 0), '3'), + DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 1, 0, 0), '4'), + DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 1, 0, 0), '5'), + DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 0, 0, 0), '6'), + DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 1, 0, 0), '7'), + DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 1, 0, 0), '8'), + DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 0, 0, 0), '9'), + + DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 1, 0, 0), '.'), + DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 1, 0, 0), '+'), + DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 1, 0, 0), '-'), + DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 1, 0, 0), '*'), + DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 0, 0, 0), '/'), + DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 1, 0, 0), '('), + DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 1, 0, 0), ')'), + + DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 1, 0, 0), '&'), + DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 1, 0, 0), '#'), + + DO(BRLAPI_DOTS(0, 0, 0, 0, 0, 1, 0, 0), ','), + DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 1, 0, 0), ';'), + DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 1, 0, 0), ':'), + DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 1, 0, 0), '!'), + DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 1, 0, 0), '?'), + DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 0, 0, 0), '"'), + DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 0, 0, 0), '\''), + DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 0, 0), '`'), + DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 1, 0), '^'), + DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 0, 0), '~'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 1, 0), '['), + DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 1, 0), ']'), + DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 0, 0), '{'), + DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 0, 0), '}'), + DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 1, 0, 0), '='), + DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 1, 0, 0), '<'), + DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 0, 0, 0), '>'), + DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 1, 0, 0), '$'), + DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 1, 0, 0), '%'), + DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 1, 0), '@'), + DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 0, 0), '|'), + DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 1, 0), '\\'), + DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 1, 0, 0), '_'), }; +/* The guest OS has started discussing with us, finish initializing BrlAPI */ +static int baum_deferred_init(BaumDriverState *baum) +{ +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + SDL_SysWMinfo info; +#endif +#endif + int tty; + + if (baum->deferred_init) { + return 1; + } + + if (brlapi__getDisplaySize(baum->brlapi, &baum->x, &baum->y) == -1) { + brlapi_perror("baum: brlapi__getDisplaySize"); + return 0; + } + +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + memset(&info, 0, sizeof(info)); + SDL_VERSION(&info.version); + if (SDL_GetWMInfo(&info)) { + tty = info.info.x11.wmwindow; + } else { +#endif +#endif + tty = BRLAPI_TTY_DEFAULT; +#if defined(CONFIG_SDL) +#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) + } +#endif +#endif + + if (brlapi__enterTtyMode(baum->brlapi, tty, NULL) == -1) { + brlapi_perror("baum: brlapi__enterTtyMode"); + return 0; + } + baum->deferred_init = 1; + return 1; +} + /* The serial port can receive more of our data */ static void baum_accept_input(struct CharDriverState *chr) { @@ -346,8 +397,10 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) cursor = i + 1; c &= ~(BRLAPI_DOT7|BRLAPI_DOT8); } - if (!(c = nabcc_translation[c])) + c = nabcc_translation[DOTS2ASCII][c]; + if (!c) { c = '?'; + } text[i] = c; } timer_del(baum->cellCount_timer); @@ -440,6 +493,8 @@ static int baum_write(CharDriverState *chr, const uint8_t *buf, int len) return 0; if (!baum->brlapi) return len; + if (!baum_deferred_init(baum)) + return len; while (len) { /* Complete our buffer as much as possible */ @@ -476,6 +531,13 @@ static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) { baum_write_packet(baum, packet, sizeof(packet)); } +static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value, + uint8_t value2) { + uint8_t packet[] = { type, value, value2 }; + DPRINTF("writing key %x %x\n", type, value); + baum_write_packet(baum, packet, sizeof(packet)); +} + /* We got some data on the BrlAPI socket */ static void baum_chr_read(void *opaque) { @@ -484,6 +546,8 @@ static void baum_chr_read(void *opaque) int ret; if (!baum->brlapi) return; + if (!baum_deferred_init(baum)) + return; while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) { DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code); /* Emulate */ @@ -540,7 +604,17 @@ static void baum_chr_read(void *opaque) } break; case BRLAPI_KEY_TYPE_SYM: - break; + { + brlapi_keyCode_t keysym = code & BRLAPI_KEY_CODE_MASK; + if (keysym < 0x100) { + uint8_t dots = nabcc_translation[ASCII2DOTS][keysym]; + if (dots) { + baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, dots); + baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, 0); + } + } + break; + } } } if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) { @@ -573,12 +647,6 @@ static CharDriverState *chr_baum_init(const char *id, BaumDriverState *baum; CharDriverState *chr; brlapi_handle_t *handle; -#if defined(CONFIG_SDL) -#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) - SDL_SysWMinfo info; -#endif -#endif - int tty; chr = qemu_chr_alloc(common, errp); if (!chr) { @@ -601,39 +669,14 @@ static CharDriverState *chr_baum_init(const char *id, brlapi_strerror(brlapi_error_location())); goto fail_handle; } + baum->deferred_init = 0; baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); - if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) { - error_setg(errp, "brlapi__getDisplaySize: %s", - brlapi_strerror(brlapi_error_location())); - goto fail; - } - -#if defined(CONFIG_SDL) -#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) - memset(&info, 0, sizeof(info)); - SDL_VERSION(&info.version); - if (SDL_GetWMInfo(&info)) - tty = info.info.x11.wmwindow; - else -#endif -#endif - tty = BRLAPI_TTY_DEFAULT; - - if (brlapi__enterTtyMode(handle, tty, NULL) == -1) { - error_setg(errp, "brlapi__enterTtyMode: %s", - brlapi_strerror(brlapi_error_location())); - goto fail; - } - qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); return chr; -fail: - timer_free(baum->cellCount_timer); - brlapi__closeConnection(handle); fail_handle: g_free(handle); g_free(chr); @@ -2082,7 +2082,7 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, * to all devices. * */ -int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) +int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp) { int ret = -1; BlockReopenQueueEntry *bs_entry, *next; @@ -2090,7 +2090,9 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) assert(bs_queue != NULL); + aio_context_release(ctx); bdrv_drain_all(); + aio_context_acquire(ctx); QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { @@ -2131,7 +2133,7 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) Error *local_err = NULL; BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); - ret = bdrv_reopen_multiple(queue, &local_err); + ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); } diff --git a/block/backup.c b/block/backup.c index 02dbe48035..81d4042ae8 100644 --- a/block/backup.c +++ b/block/backup.c @@ -300,6 +300,21 @@ void backup_cow_request_end(CowRequest *req) cow_request_end(req); } +static void backup_drain(BlockJob *job) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + + /* Need to keep a reference in case blk_drain triggers execution + * of backup_complete... + */ + if (s->target) { + BlockBackend *target = s->target; + blk_ref(target); + blk_drain(target); + blk_unref(target); + } +} + static const BlockJobDriver backup_job_driver = { .instance_size = sizeof(BackupBlockJob), .job_type = BLOCK_JOB_TYPE_BACKUP, @@ -307,6 +322,7 @@ static const BlockJobDriver backup_job_driver = { .commit = backup_commit, .abort = backup_abort, .attached_aio_context = backup_attached_aio_context, + .drain = backup_drain, }; static BlockErrorAction backup_error_action(BackupBlockJob *job, @@ -331,6 +347,7 @@ static void backup_complete(BlockJob *job, void *opaque) BackupCompleteData *data = opaque; blk_unref(s->target); + s->target = NULL; block_job_completed(job, data->ret); g_free(data); diff --git a/block/block-backend.c b/block/block-backend.c index 1a724a8d89..27a7f6f523 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -799,20 +799,25 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, BdrvRequestFlags flags) { int ret; + BlockDriverState *bs = blk_bs(blk); - trace_blk_co_preadv(blk, blk_bs(blk), offset, bytes, flags); + trace_blk_co_preadv(blk, bs, offset, bytes, flags); ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { return ret; } + bdrv_inc_in_flight(bs); + /* throttling disk I/O */ if (blk->public.throttle_state) { throttle_group_co_io_limits_intercept(blk, bytes, false); } - return bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); + ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); + bdrv_dec_in_flight(bs); + return ret; } int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, @@ -820,14 +825,17 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, BdrvRequestFlags flags) { int ret; + BlockDriverState *bs = blk_bs(blk); - trace_blk_co_pwritev(blk, blk_bs(blk), offset, bytes, flags); + trace_blk_co_pwritev(blk, bs, offset, bytes, flags); ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { return ret; } + bdrv_inc_in_flight(bs); + /* throttling disk I/O */ if (blk->public.throttle_state) { throttle_group_co_io_limits_intercept(blk, bytes, true); @@ -837,7 +845,9 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, flags |= BDRV_REQ_FUA; } - return bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags); + ret = bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags); + bdrv_dec_in_flight(bs); + return ret; } typedef struct BlkRwCo { @@ -868,7 +878,6 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, int64_t bytes, CoroutineEntry co_entry, BdrvRequestFlags flags) { - AioContext *aio_context; QEMUIOVector qiov; struct iovec iov; Coroutine *co; @@ -890,11 +899,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, co = qemu_coroutine_create(co_entry, &rwco); qemu_coroutine_enter(co); - - aio_context = blk_get_aio_context(blk); - while (rwco.ret == NOT_DONE) { - aio_poll(aio_context, true); - } + BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE); return rwco.ret; } @@ -930,6 +935,8 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) static void error_callback_bh(void *opaque) { struct BlockBackendAIOCB *acb = opaque; + + bdrv_dec_in_flight(acb->common.bs); acb->common.cb(acb->common.opaque, acb->ret); qemu_aio_unref(acb); } @@ -940,6 +947,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, { struct BlockBackendAIOCB *acb; + bdrv_inc_in_flight(blk_bs(blk)); acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); acb->blk = blk; acb->ret = ret; @@ -962,6 +970,7 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { static void blk_aio_complete(BlkAioEmAIOCB *acb) { if (acb->has_returned) { + bdrv_dec_in_flight(acb->common.bs); acb->common.cb(acb->common.opaque, acb->rwco.ret); qemu_aio_unref(acb); } @@ -983,6 +992,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, BlkAioEmAIOCB *acb; Coroutine *co; + bdrv_inc_in_flight(blk_bs(blk)); acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); acb->rwco = (BlkRwCo) { .blk = blk, @@ -1099,26 +1109,36 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, blk_aio_write_entry, flags, cb, opaque); } +static void blk_aio_flush_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_flush(rwco->blk); + blk_aio_complete(acb); +} + BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque) { - if (!blk_is_available(blk)) { - return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); - } + return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); +} + +static void blk_aio_pdiscard_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; - return bdrv_aio_flush(blk_bs(blk), cb, opaque); + rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, acb->bytes); + blk_aio_complete(acb); } BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int count, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_byte_request(blk, offset, count); - if (ret < 0) { - return blk_abort_aio_request(blk, cb, opaque, ret); - } - - return bdrv_aio_pdiscard(blk_bs(blk), offset, count, cb, opaque); + return blk_aio_prwv(blk, offset, count, NULL, blk_aio_pdiscard_entry, 0, + cb, opaque); } void blk_aio_cancel(BlockAIOCB *acb) @@ -1131,23 +1151,50 @@ void blk_aio_cancel_async(BlockAIOCB *acb) bdrv_aio_cancel_async(acb); } -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { if (!blk_is_available(blk)) { return -ENOMEDIUM; } - return bdrv_ioctl(blk_bs(blk), req, buf); + return bdrv_co_ioctl(blk_bs(blk), req, buf); +} + +static void blk_ioctl_entry(void *opaque) +{ + BlkRwCo *rwco = opaque; + rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, + rwco->qiov->iov[0].iov_base); +} + +int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +{ + return blk_prw(blk, req, buf, 0, blk_ioctl_entry, 0); +} + +static void blk_aio_ioctl_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, + rwco->qiov->iov[0].iov_base); + blk_aio_complete(acb); } BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) { - if (!blk_is_available(blk)) { - return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); - } + QEMUIOVector qiov; + struct iovec iov; + + iov = (struct iovec) { + .iov_base = buf, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); - return bdrv_aio_ioctl(blk_bs(blk), req, buf, cb, opaque); + return blk_aio_prwv(blk, req, 0, &qiov, blk_aio_ioctl_entry, 0, cb, opaque); } int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int count) @@ -1169,13 +1216,15 @@ int blk_co_flush(BlockBackend *blk) return bdrv_co_flush(blk_bs(blk)); } -int blk_flush(BlockBackend *blk) +static void blk_flush_entry(void *opaque) { - if (!blk_is_available(blk)) { - return -ENOMEDIUM; - } + BlkRwCo *rwco = opaque; + rwco->ret = blk_co_flush(rwco->blk); +} - return bdrv_flush(blk_bs(blk)); +int blk_flush(BlockBackend *blk) +{ + return blk_prw(blk, 0, NULL, 0, blk_flush_entry, 0); } void blk_drain(BlockBackend *blk) @@ -1555,14 +1604,15 @@ int blk_truncate(BlockBackend *blk, int64_t offset) return bdrv_truncate(blk_bs(blk), offset); } -int blk_pdiscard(BlockBackend *blk, int64_t offset, int count) +static void blk_pdiscard_entry(void *opaque) { - int ret = blk_check_byte_request(blk, offset, count); - if (ret < 0) { - return ret; - } + BlkRwCo *rwco = opaque; + rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, rwco->qiov->size); +} - return bdrv_pdiscard(blk_bs(blk), offset, count); +int blk_pdiscard(BlockBackend *blk, int64_t offset, int count) +{ + return blk_prw(blk, offset, NULL, count, blk_pdiscard_entry, 0); } int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, diff --git a/block/commit.c b/block/commit.c index 9f67a8b121..499eccaeee 100644 --- a/block/commit.c +++ b/block/commit.c @@ -251,7 +251,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, orig_overlay_flags | BDRV_O_RDWR); } if (reopen_queue) { - bdrv_reopen_multiple(reopen_queue, &local_err); + bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); block_job_unref(&s->common); diff --git a/block/io.c b/block/io.c index b136c89ae0..be0d862ca6 100644 --- a/block/io.c +++ b/block/io.c @@ -143,7 +143,7 @@ bool bdrv_requests_pending(BlockDriverState *bs) { BdrvChild *child; - if (!QLIST_EMPTY(&bs->tracked_requests)) { + if (atomic_read(&bs->in_flight)) { return true; } @@ -156,16 +156,22 @@ bool bdrv_requests_pending(BlockDriverState *bs) return false; } -static void bdrv_drain_recurse(BlockDriverState *bs) +static bool bdrv_drain_recurse(BlockDriverState *bs) { BdrvChild *child; + bool waited; + + waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); if (bs->drv && bs->drv->bdrv_drain) { bs->drv->bdrv_drain(bs); } + QLIST_FOREACH(child, &bs->children, next) { - bdrv_drain_recurse(child->bs); + waited |= bdrv_drain_recurse(child->bs); } + + return waited; } typedef struct { @@ -174,23 +180,14 @@ typedef struct { bool done; } BdrvCoDrainData; -static void bdrv_drain_poll(BlockDriverState *bs) -{ - bool busy = true; - - while (busy) { - /* Keep iterating */ - busy = bdrv_requests_pending(bs); - busy |= aio_poll(bdrv_get_aio_context(bs), busy); - } -} - static void bdrv_co_drain_bh_cb(void *opaque) { BdrvCoDrainData *data = opaque; Coroutine *co = data->co; + BlockDriverState *bs = data->bs; - bdrv_drain_poll(data->bs); + bdrv_dec_in_flight(bs); + bdrv_drained_begin(bs); data->done = true; qemu_coroutine_enter(co); } @@ -209,6 +206,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) .bs = bs, .done = false, }; + bdrv_inc_in_flight(bs); aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), bdrv_co_drain_bh_cb, &data); @@ -220,6 +218,11 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) void bdrv_drained_begin(BlockDriverState *bs) { + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(bs); + return; + } + if (!bs->quiesce_counter++) { aio_disable_external(bdrv_get_aio_context(bs)); bdrv_parent_drained_begin(bs); @@ -227,11 +230,6 @@ void bdrv_drained_begin(BlockDriverState *bs) bdrv_io_unplugged_begin(bs); bdrv_drain_recurse(bs); - if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs); - } else { - bdrv_drain_poll(bs); - } bdrv_io_unplugged_end(bs); } @@ -279,7 +277,7 @@ void bdrv_drain(BlockDriverState *bs) void bdrv_drain_all(void) { /* Always run first iteration so any pending completion BHs run */ - bool busy = true; + bool waited = true; BlockDriverState *bs; BdrvNextIterator it; BlockJob *job = NULL; @@ -299,7 +297,6 @@ void bdrv_drain_all(void) aio_context_acquire(aio_context); bdrv_parent_drained_begin(bs); bdrv_io_unplugged_begin(bs); - bdrv_drain_recurse(bs); aio_context_release(aio_context); if (!g_slist_find(aio_ctxs, aio_context)) { @@ -313,8 +310,8 @@ void bdrv_drain_all(void) * request completion. Therefore we must keep looping until there was no * more activity rather than simply draining each device independently. */ - while (busy) { - busy = false; + while (waited) { + waited = false; for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) { AioContext *aio_context = ctx->data; @@ -322,13 +319,9 @@ void bdrv_drain_all(void) aio_context_acquire(aio_context); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { if (aio_context == bdrv_get_aio_context(bs)) { - if (bdrv_requests_pending(bs)) { - busy = true; - aio_poll(aio_context, busy); - } + waited |= bdrv_drain_recurse(bs); } } - busy |= aio_poll(aio_context, false); aio_context_release(aio_context); } } @@ -476,6 +469,28 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req, return true; } +void bdrv_inc_in_flight(BlockDriverState *bs) +{ + atomic_inc(&bs->in_flight); +} + +static void dummy_bh_cb(void *opaque) +{ +} + +void bdrv_wakeup(BlockDriverState *bs) +{ + if (bs->wakeup) { + aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); + } +} + +void bdrv_dec_in_flight(BlockDriverState *bs) +{ + atomic_dec(&bs->in_flight); + bdrv_wakeup(bs); +} + static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self) { BlockDriverState *bs = self->bs; @@ -583,13 +598,9 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset, /* Fast-path if already in coroutine context */ bdrv_rw_co_entry(&rwco); } else { - AioContext *aio_context = bdrv_get_aio_context(child->bs); - co = qemu_coroutine_create(bdrv_rw_co_entry, &rwco); qemu_coroutine_enter(co); - while (rwco.ret == NOT_DONE) { - aio_poll(aio_context, true); - } + BDRV_POLL_WHILE(child->bs, rwco.ret == NOT_DONE); } return rwco.ret; } @@ -1097,6 +1108,8 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child, return ret; } + bdrv_inc_in_flight(bs); + /* Don't do copy-on-read if we read data before write operation */ if (bs->copy_on_read && !(flags & BDRV_REQ_NO_SERIALISING)) { flags |= BDRV_REQ_COPY_ON_READ; @@ -1132,6 +1145,7 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child, use_local_qiov ? &local_qiov : qiov, flags); tracked_request_end(&req); + bdrv_dec_in_flight(bs); if (use_local_qiov) { qemu_iovec_destroy(&local_qiov); @@ -1480,6 +1494,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, return ret; } + bdrv_inc_in_flight(bs); /* * Align write if necessary by performing a read-modify-write cycle. * Pad qiov with the read parts and be sure to have a tracked request not @@ -1581,6 +1596,7 @@ fail: qemu_vfree(tail_buf); out: tracked_request_end(&req); + bdrv_dec_in_flight(bs); return ret; } @@ -1705,17 +1721,19 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, } *file = NULL; + bdrv_inc_in_flight(bs); ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum, file); if (ret < 0) { *pnum = 0; - return ret; + goto out; } if (ret & BDRV_BLOCK_RAW) { assert(ret & BDRV_BLOCK_OFFSET_VALID); - return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, - *pnum, pnum, file); + ret = bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, + *pnum, pnum, file); + goto out; } if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { @@ -1757,6 +1775,8 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, } } +out: + bdrv_dec_in_flight(bs); return ret; } @@ -1822,14 +1842,10 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs, /* Fast-path if already in coroutine context */ bdrv_get_block_status_above_co_entry(&data); } else { - AioContext *aio_context = bdrv_get_aio_context(bs); - co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry, &data); qemu_coroutine_enter(co); - while (!data.done) { - aio_poll(aio_context, true); - } + BDRV_POLL_WHILE(bs, !data.done); } return data.ret; } @@ -2102,6 +2118,7 @@ static const AIOCBInfo bdrv_em_co_aiocb_info = { static void bdrv_co_complete(BlockAIOCBCoroutine *acb) { if (!acb->need_bh) { + bdrv_dec_in_flight(acb->common.bs); acb->common.cb(acb->common.opaque, acb->req.error); qemu_aio_unref(acb); } @@ -2152,6 +2169,9 @@ static BlockAIOCB *bdrv_co_aio_prw_vector(BdrvChild *child, Coroutine *co; BlockAIOCBCoroutine *acb; + /* Matched by bdrv_co_complete's bdrv_dec_in_flight. */ + bdrv_inc_in_flight(child->bs); + acb = qemu_aio_get(&bdrv_em_co_aiocb_info, child->bs, cb, opaque); acb->child = child; acb->need_bh = true; @@ -2185,6 +2205,9 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, Coroutine *co; BlockAIOCBCoroutine *acb; + /* Matched by bdrv_co_complete's bdrv_dec_in_flight. */ + bdrv_inc_in_flight(bs); + acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); acb->need_bh = true; acb->req.error = -EINPROGRESS; @@ -2196,35 +2219,6 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, return &acb->common; } -static void coroutine_fn bdrv_aio_pdiscard_co_entry(void *opaque) -{ - BlockAIOCBCoroutine *acb = opaque; - BlockDriverState *bs = acb->common.bs; - - acb->req.error = bdrv_co_pdiscard(bs, acb->req.offset, acb->req.bytes); - bdrv_co_complete(acb); -} - -BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs, int64_t offset, int count, - BlockCompletionFunc *cb, void *opaque) -{ - Coroutine *co; - BlockAIOCBCoroutine *acb; - - trace_bdrv_aio_pdiscard(bs, offset, count, opaque); - - acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); - acb->need_bh = true; - acb->req.error = -EINPROGRESS; - acb->req.offset = offset; - acb->req.bytes = count; - co = qemu_coroutine_create(bdrv_aio_pdiscard_co_entry, acb); - qemu_coroutine_enter(co); - - bdrv_co_maybe_schedule_bh(acb); - return &acb->common; -} - void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque) { @@ -2273,23 +2267,22 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque) int coroutine_fn bdrv_co_flush(BlockDriverState *bs) { int ret; - BdrvTrackedRequest req; if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) || bdrv_is_sg(bs)) { return 0; } - tracked_request_begin(&req, bs, 0, 0, BDRV_TRACKED_FLUSH); + bdrv_inc_in_flight(bs); int current_gen = bs->write_gen; /* Wait until any previous flushes are completed */ - while (bs->active_flush_req != NULL) { + while (bs->active_flush_req) { qemu_co_queue_wait(&bs->flush_queue); } - bs->active_flush_req = &req; + bs->active_flush_req = true; /* Write back all layers by calling one driver function */ if (bs->drv->bdrv_co_flush) { @@ -2359,11 +2352,11 @@ flush_parent: out: /* Notify any pending flushes that we have completed */ bs->flushed_gen = current_gen; - bs->active_flush_req = NULL; + bs->active_flush_req = false; /* Return value is ignored - it's ok if wait queue is empty */ qemu_co_queue_next(&bs->flush_queue); - tracked_request_end(&req); + bdrv_dec_in_flight(bs); return ret; } @@ -2379,13 +2372,9 @@ int bdrv_flush(BlockDriverState *bs) /* Fast-path if already in coroutine context */ bdrv_flush_co_entry(&flush_co); } else { - AioContext *aio_context = bdrv_get_aio_context(bs); - co = qemu_coroutine_create(bdrv_flush_co_entry, &flush_co); qemu_coroutine_enter(co); - while (flush_co.ret == NOT_DONE) { - aio_poll(aio_context, true); - } + BDRV_POLL_WHILE(bs, flush_co.ret == NOT_DONE); } return flush_co.ret; @@ -2446,6 +2435,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, return 0; } + bdrv_inc_in_flight(bs); tracked_request_begin(&req, bs, offset, count, BDRV_TRACKED_DISCARD); ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req); @@ -2492,6 +2482,7 @@ out: bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS, req.bytes >> BDRV_SECTOR_BITS); tracked_request_end(&req); + bdrv_dec_in_flight(bs); return ret; } @@ -2509,106 +2500,41 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count) /* Fast-path if already in coroutine context */ bdrv_pdiscard_co_entry(&rwco); } else { - AioContext *aio_context = bdrv_get_aio_context(bs); - co = qemu_coroutine_create(bdrv_pdiscard_co_entry, &rwco); qemu_coroutine_enter(co); - while (rwco.ret == NOT_DONE) { - aio_poll(aio_context, true); - } + BDRV_POLL_WHILE(bs, rwco.ret == NOT_DONE); } return rwco.ret; } -static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf) +int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) { BlockDriver *drv = bs->drv; - BdrvTrackedRequest tracked_req; CoroutineIOCompletion co = { .coroutine = qemu_coroutine_self(), }; BlockAIOCB *acb; - tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL); - if (!drv || !drv->bdrv_aio_ioctl) { - co.ret = -ENOTSUP; - goto out; - } - - acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); - if (!acb) { + bdrv_inc_in_flight(bs); + if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) { co.ret = -ENOTSUP; goto out; } - qemu_coroutine_yield(); -out: - tracked_request_end(&tracked_req); - return co.ret; -} - -typedef struct { - BlockDriverState *bs; - int req; - void *buf; - int ret; -} BdrvIoctlCoData; - -static void coroutine_fn bdrv_co_ioctl_entry(void *opaque) -{ - BdrvIoctlCoData *data = opaque; - data->ret = bdrv_co_do_ioctl(data->bs, data->req, data->buf); -} - -/* needed for generic scsi interface */ -int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) -{ - BdrvIoctlCoData data = { - .bs = bs, - .req = req, - .buf = buf, - .ret = -EINPROGRESS, - }; - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_co_ioctl_entry(&data); + if (drv->bdrv_co_ioctl) { + co.ret = drv->bdrv_co_ioctl(bs, req, buf); } else { - Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry, &data); - - qemu_coroutine_enter(co); - while (data.ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(bs), true); + acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); + if (!acb) { + co.ret = -ENOTSUP; + goto out; } + qemu_coroutine_yield(); } - return data.ret; -} - -static void coroutine_fn bdrv_co_aio_ioctl_entry(void *opaque) -{ - BlockAIOCBCoroutine *acb = opaque; - acb->req.error = bdrv_co_do_ioctl(acb->common.bs, - acb->req.req, acb->req.buf); - bdrv_co_complete(acb); -} - -BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque) -{ - BlockAIOCBCoroutine *acb = qemu_aio_get(&bdrv_em_co_aiocb_info, - bs, cb, opaque); - Coroutine *co; - - acb->need_bh = true; - acb->req.error = -EINPROGRESS; - acb->req.req = req; - acb->req.buf = buf; - co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry, acb); - qemu_coroutine_enter(co); - - bdrv_co_maybe_schedule_bh(acb); - return &acb->common; +out: + bdrv_dec_in_flight(bs); + return co.ret; } void *qemu_blockalign(BlockDriverState *bs, size_t size) diff --git a/block/mirror.c b/block/mirror.c index a433e6848c..3a0788ede3 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -469,7 +469,11 @@ static void mirror_free_init(MirrorBlockJob *s) } } -static void mirror_drain(MirrorBlockJob *s) +/* This is also used for the .pause callback. There is no matching + * mirror_resume() because mirror_run() will begin iterating again + * when the job is resumed. + */ +static void mirror_wait_for_all_io(MirrorBlockJob *s) { while (s->in_flight > 0) { mirror_wait_for_io(s); @@ -528,6 +532,7 @@ static void mirror_exit(BlockJob *job, void *opaque) g_free(s->replaces); bdrv_op_unblock_all(target_bs, s->common.blocker); blk_unref(s->target); + s->target = NULL; block_job_completed(&s->common, data->ret); g_free(data); bdrv_drained_end(src); @@ -582,7 +587,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) sector_num += nb_sectors; } - mirror_drain(s); + mirror_wait_for_all_io(s); } /* First part, loop on the sectors and initialize the dirty bitmap. */ @@ -617,6 +622,7 @@ static void coroutine_fn mirror_run(void *opaque) MirrorExitData *data; BlockDriverState *bs = blk_bs(s->common.blk); BlockDriverState *target_bs = blk_bs(s->target); + bool need_drain = true; int64_t length; BlockDriverInfo bdi; char backing_filename[2]; /* we only need 2 characters because we are only @@ -752,11 +758,26 @@ static void coroutine_fn mirror_run(void *opaque) * source has dirty data to copy! * * Note that I/O can be submitted by the guest while - * mirror_populate runs. + * mirror_populate runs, so pause it now. Before deciding + * whether to switch to target check one last time if I/O has + * come in the meanwhile, and if not flush the data to disk. */ trace_mirror_before_drain(s, cnt); - bdrv_co_drain(bs); + + bdrv_drained_begin(bs); cnt = bdrv_get_dirty_count(s->dirty_bitmap); + if (cnt > 0) { + bdrv_drained_end(bs); + continue; + } + + /* The two disks are in sync. Exit and report successful + * completion. + */ + assert(QLIST_EMPTY(&bs->tracked_requests)); + s->common.cancelled = false; + need_drain = false; + break; } ret = 0; @@ -769,13 +790,6 @@ static void coroutine_fn mirror_run(void *opaque) } else if (!should_complete) { delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); - } else if (cnt == 0) { - /* The two disks are in sync. Exit and report successful - * completion. - */ - assert(QLIST_EMPTY(&bs->tracked_requests)); - s->common.cancelled = false; - break; } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } @@ -787,7 +801,8 @@ immediate_exit: * the target is a copy of the source. */ assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common))); - mirror_drain(s); + assert(need_drain); + mirror_wait_for_all_io(s); } assert(s->in_flight == 0); @@ -799,9 +814,10 @@ immediate_exit: data = g_malloc(sizeof(*data)); data->ret = ret; - /* Before we switch to target in mirror_exit, make sure data doesn't - * change. */ - bdrv_drained_begin(bs); + + if (need_drain) { + bdrv_drained_begin(bs); + } block_job_defer_to_main_loop(&s->common, mirror_exit, data); } @@ -872,14 +888,11 @@ static void mirror_complete(BlockJob *job, Error **errp) block_job_enter(&s->common); } -/* There is no matching mirror_resume() because mirror_run() will begin - * iterating again when the job is resumed. - */ -static void coroutine_fn mirror_pause(BlockJob *job) +static void mirror_pause(BlockJob *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); - mirror_drain(s); + mirror_wait_for_all_io(s); } static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context) @@ -889,6 +902,21 @@ static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context) blk_set_aio_context(s->target, new_context); } +static void mirror_drain(BlockJob *job) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + /* Need to keep a reference in case blk_drain triggers execution + * of mirror_complete... + */ + if (s->target) { + BlockBackend *target = s->target; + blk_ref(target); + blk_drain(target); + blk_unref(target); + } +} + static const BlockJobDriver mirror_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = BLOCK_JOB_TYPE_MIRROR, @@ -896,6 +924,7 @@ static const BlockJobDriver mirror_job_driver = { .complete = mirror_complete, .pause = mirror_pause, .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, }; static const BlockJobDriver commit_active_job_driver = { @@ -905,6 +934,7 @@ static const BlockJobDriver commit_active_job_driver = { .complete = mirror_complete, .pause = mirror_pause, .attached_aio_context = mirror_attached_aio_context, + .drain = mirror_drain, }; static void mirror_start_job(const char *job_id, BlockDriverState *bs, diff --git a/block/nbd.c b/block/nbd.c index 6bc06d6198..6e837f80c9 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -32,6 +32,9 @@ #include "qemu/uri.h" #include "block/block_int.h" #include "qemu/module.h" +#include "qapi-visit.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qint.h" @@ -44,7 +47,8 @@ typedef struct BDRVNBDState { NbdClientSession client; /* For nbd_refresh_filename() */ - char *path, *host, *port, *export, *tlscredsid; + SocketAddress *saddr; + char *export, *tlscredsid; } BDRVNBDState; static int nbd_parse_uri(const char *filename, QDict *options) @@ -90,9 +94,13 @@ static int nbd_parse_uri(const char *filename, QDict *options) ret = -EINVAL; goto out; } - qdict_put(options, "path", qstring_from_str(qp->p[0].value)); + qdict_put(options, "server.type", qstring_from_str("unix")); + qdict_put(options, "server.data.path", + qstring_from_str(qp->p[0].value)); } else { QString *host; + char *port_str; + /* nbd[+tcp]://host[:port]/export */ if (!uri->server) { ret = -EINVAL; @@ -107,12 +115,12 @@ static int nbd_parse_uri(const char *filename, QDict *options) host = qstring_from_str(uri->server); } - qdict_put(options, "host", host); - if (uri->port) { - char* port_str = g_strdup_printf("%d", uri->port); - qdict_put(options, "port", qstring_from_str(port_str)); - g_free(port_str); - } + qdict_put(options, "server.type", qstring_from_str("inet")); + qdict_put(options, "server.data.host", host); + + port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); + qdict_put(options, "server.data.port", qstring_from_str(port_str)); + g_free(port_str); } out: @@ -123,6 +131,26 @@ out: return ret; } +static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) +{ + const QDictEntry *e; + + for (e = qdict_first(options); e; e = qdict_next(options, e)) { + if (!strcmp(e->key, "host") || + !strcmp(e->key, "port") || + !strcmp(e->key, "path") || + !strcmp(e->key, "export") || + strstart(e->key, "server.", NULL)) + { + error_setg(errp, "Option '%s' cannot be used with a file name", + e->key); + return true; + } + } + + return false; +} + static void nbd_parse_filename(const char *filename, QDict *options, Error **errp) { @@ -131,12 +159,7 @@ static void nbd_parse_filename(const char *filename, QDict *options, const char *host_spec; const char *unixpath; - if (qdict_haskey(options, "host") - || qdict_haskey(options, "port") - || qdict_haskey(options, "path")) - { - error_setg(errp, "host/port/path and a file name may not be specified " - "at the same time"); + if (nbd_has_filename_options_conflict(options, errp)) { return; } @@ -173,7 +196,8 @@ static void nbd_parse_filename(const char *filename, QDict *options, /* are we a UNIX or TCP socket? */ if (strstart(host_spec, "unix:", &unixpath)) { - qdict_put(options, "path", qstring_from_str(unixpath)); + qdict_put(options, "server.type", qstring_from_str("unix")); + qdict_put(options, "server.data.path", qstring_from_str(unixpath)); } else { InetSocketAddress *addr = NULL; @@ -182,8 +206,9 @@ static void nbd_parse_filename(const char *filename, QDict *options, goto out; } - qdict_put(options, "host", qstring_from_str(addr->host)); - qdict_put(options, "port", qstring_from_str(addr->port)); + qdict_put(options, "server.type", qstring_from_str("inet")); + qdict_put(options, "server.data.host", qstring_from_str(addr->host)); + qdict_put(options, "server.data.port", qstring_from_str(addr->port)); qapi_free_InetSocketAddress(addr); } @@ -191,47 +216,81 @@ out: g_free(file); } -static SocketAddress *nbd_config(BDRVNBDState *s, QemuOpts *opts, Error **errp) +static bool nbd_process_legacy_socket_options(QDict *output_options, + QemuOpts *legacy_opts, + Error **errp) { - SocketAddress *saddr; + const char *path = qemu_opt_get(legacy_opts, "path"); + const char *host = qemu_opt_get(legacy_opts, "host"); + const char *port = qemu_opt_get(legacy_opts, "port"); + const QDictEntry *e; - s->path = g_strdup(qemu_opt_get(opts, "path")); - s->host = g_strdup(qemu_opt_get(opts, "host")); + if (!path && !host && !port) { + return true; + } - if (!s->path == !s->host) { - if (s->path) { - error_setg(errp, "path and host may not be used at the same time."); - } else { - error_setg(errp, "one of path and host must be specified."); + for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) + { + if (strstart(e->key, "server.", NULL)) { + error_setg(errp, "Cannot use 'server' and path/host/port at the " + "same time"); + return false; } - return NULL; } - saddr = g_new0(SocketAddress, 1); + if (path && host) { + error_setg(errp, "path and host may not be used at the same time"); + return false; + } else if (path) { + if (port) { + error_setg(errp, "port may not be used without host"); + return false; + } - if (s->path) { - UnixSocketAddress *q_unix; - saddr->type = SOCKET_ADDRESS_KIND_UNIX; - q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); - q_unix->path = g_strdup(s->path); - } else { - InetSocketAddress *inet; + qdict_put(output_options, "server.type", qstring_from_str("unix")); + qdict_put(output_options, "server.data.path", qstring_from_str(path)); + } else if (host) { + qdict_put(output_options, "server.type", qstring_from_str("inet")); + qdict_put(output_options, "server.data.host", qstring_from_str(host)); + qdict_put(output_options, "server.data.port", + qstring_from_str(port ?: stringify(NBD_DEFAULT_PORT))); + } - s->port = g_strdup(qemu_opt_get(opts, "port")); + return true; +} - saddr->type = SOCKET_ADDRESS_KIND_INET; - inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); - inet->host = g_strdup(s->host); - inet->port = g_strdup(s->port); - if (!inet->port) { - inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); - } +static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp) +{ + SocketAddress *saddr = NULL; + QDict *addr = NULL; + QObject *crumpled_addr = NULL; + Visitor *iv = NULL; + Error *local_err = NULL; + + qdict_extract_subqdict(options, &addr, "server."); + if (!qdict_size(addr)) { + error_setg(errp, "NBD server address missing"); + goto done; } - s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + crumpled_addr = qdict_crumple(addr, errp); + if (!crumpled_addr) { + goto done; + } - s->export = g_strdup(qemu_opt_get(opts, "export")); + iv = qobject_input_visitor_new(crumpled_addr, true); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto done; + } + + s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; +done: + QDECREF(addr); + qobject_decref(crumpled_addr); + visit_free(iv); return saddr; } @@ -248,6 +307,7 @@ static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, Error *local_err = NULL; sioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); qio_channel_socket_connect_sync(sioc, saddr, @@ -332,7 +392,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts = NULL; Error *local_err = NULL; QIOChannelSocket *sioc = NULL; - SocketAddress *saddr = NULL; QCryptoTLSCreds *tlscreds = NULL; const char *hostname = NULL; int ret = -EINVAL; @@ -344,12 +403,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto error; } + /* Translate @host, @port, and @path to a SocketAddress */ + if (!nbd_process_legacy_socket_options(options, opts, errp)) { + goto error; + } + /* Pop the config into our state object. Exit if invalid. */ - saddr = nbd_config(s, opts, errp); - if (!saddr) { + s->saddr = nbd_config(s, options, errp); + if (!s->saddr) { goto error; } + s->export = g_strdup(qemu_opt_get(opts, "export")); + s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); if (s->tlscredsid) { tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); @@ -357,17 +423,17 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto error; } - if (saddr->type != SOCKET_ADDRESS_KIND_INET) { + if (s->saddr->type != SOCKET_ADDRESS_KIND_INET) { error_setg(errp, "TLS only supported over IP sockets"); goto error; } - hostname = saddr->u.inet.data->host; + hostname = s->saddr->u.inet.data->host; } /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sioc = nbd_establish_connection(saddr, errp); + sioc = nbd_establish_connection(s->saddr, errp); if (!sioc) { ret = -ECONNREFUSED; goto error; @@ -384,13 +450,10 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, object_unref(OBJECT(tlscreds)); } if (ret < 0) { - g_free(s->path); - g_free(s->host); - g_free(s->port); + qapi_free_SocketAddress(s->saddr); g_free(s->export); g_free(s->tlscredsid); } - qapi_free_SocketAddress(saddr); qemu_opts_del(opts); return ret; } @@ -412,9 +475,7 @@ static void nbd_close(BlockDriverState *bs) nbd_client_close(bs); - g_free(s->path); - g_free(s->host); - g_free(s->port); + qapi_free_SocketAddress(s->saddr); g_free(s->export); g_free(s->tlscredsid); } @@ -441,45 +502,51 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) { BDRVNBDState *s = bs->opaque; QDict *opts = qdict_new(); + QObject *saddr_qdict; + Visitor *ov; + const char *host = NULL, *port = NULL, *path = NULL; + + if (s->saddr->type == SOCKET_ADDRESS_KIND_INET) { + const InetSocketAddress *inet = s->saddr->u.inet.data; + if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { + host = inet->host; + port = inet->port; + } + } else if (s->saddr->type == SOCKET_ADDRESS_KIND_UNIX) { + path = s->saddr->u.q_unix.data->path; + } - qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); + qdict_put(opts, "driver", qstring_from_str("nbd")); - if (s->path && s->export) { + if (path && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd+unix:///%s?socket=%s", s->export, s->path); - } else if (s->path && !s->export) { + "nbd+unix:///%s?socket=%s", s->export, path); + } else if (path && !s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd+unix://?socket=%s", s->path); - } else if (!s->path && s->export && s->port) { + "nbd+unix://?socket=%s", path); + } else if (host && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s:%s/%s", s->host, s->port, s->export); - } else if (!s->path && s->export && !s->port) { + "nbd://%s:%s/%s", host, port, s->export); + } else if (host && !s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s/%s", s->host, s->export); - } else if (!s->path && !s->export && s->port) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s:%s", s->host, s->port); - } else if (!s->path && !s->export && !s->port) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s", s->host); + "nbd://%s:%s", host, port); } - if (s->path) { - qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(s->path))); - } else if (s->port) { - qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); - qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(s->port))); - } else { - qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); - } + ov = qobject_output_visitor_new(&saddr_qdict); + visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); + visit_complete(ov, &saddr_qdict); + assert(qobject_type(saddr_qdict) == QTYPE_QDICT); + + qdict_put_obj(opts, "server", saddr_qdict); + if (s->export) { - qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(s->export))); + qdict_put(opts, "export", qstring_from_str(s->export)); } if (s->tlscredsid) { - qdict_put_obj(opts, "tls-creds", - QOBJECT(qstring_from_str(s->tlscredsid))); + qdict_put(opts, "tls-creds", qstring_from_str(s->tlscredsid)); } + qdict_flatten(opts); bs->full_open_options = opts; } diff --git a/block/nfs.c b/block/nfs.c index c3db2ec58d..88c60a9118 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -52,6 +52,7 @@ typedef struct NFSClient { } NFSClient; typedef struct NFSRPC { + BlockDriverState *bs; int ret; int complete; QEMUIOVector *iov; @@ -90,11 +91,12 @@ static void nfs_process_write(void *arg) nfs_set_events(client); } -static void nfs_co_init_task(NFSClient *client, NFSRPC *task) +static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) { *task = (NFSRPC) { .co = qemu_coroutine_self(), - .client = client, + .bs = bs, + .client = bs->opaque, }; } @@ -111,6 +113,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, { NFSRPC *task = private_data; task->ret = ret; + assert(!task->st); if (task->ret > 0 && task->iov) { if (task->ret <= task->iov->size) { qemu_iovec_from_buf(task->iov, 0, data, task->ret); @@ -118,18 +121,11 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, task->ret = -EIO; } } - if (task->ret == 0 && task->st) { - memcpy(task->st, data, sizeof(struct stat)); - } if (task->ret < 0) { error_report("NFS Error: %s", nfs_get_error(nfs)); } - if (task->co) { - aio_bh_schedule_oneshot(task->client->aio_context, - nfs_co_generic_bh_cb, task); - } else { - task->complete = 1; - } + aio_bh_schedule_oneshot(task->client->aio_context, + nfs_co_generic_bh_cb, task); } static int coroutine_fn nfs_co_readv(BlockDriverState *bs, @@ -139,7 +135,7 @@ static int coroutine_fn nfs_co_readv(BlockDriverState *bs, NFSClient *client = bs->opaque; NFSRPC task; - nfs_co_init_task(client, &task); + nfs_co_init_task(bs, &task); task.iov = iov; if (nfs_pread_async(client->context, client->fh, @@ -149,8 +145,8 @@ static int coroutine_fn nfs_co_readv(BlockDriverState *bs, return -ENOMEM; } + nfs_set_events(client); while (!task.complete) { - nfs_set_events(client); qemu_coroutine_yield(); } @@ -174,7 +170,7 @@ static int coroutine_fn nfs_co_writev(BlockDriverState *bs, NFSRPC task; char *buf = NULL; - nfs_co_init_task(client, &task); + nfs_co_init_task(bs, &task); buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE); if (nb_sectors && buf == NULL) { @@ -191,8 +187,8 @@ static int coroutine_fn nfs_co_writev(BlockDriverState *bs, return -ENOMEM; } + nfs_set_events(client); while (!task.complete) { - nfs_set_events(client); qemu_coroutine_yield(); } @@ -210,15 +206,15 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs) NFSClient *client = bs->opaque; NFSRPC task; - nfs_co_init_task(client, &task); + nfs_co_init_task(bs, &task); if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, &task) != 0) { return -ENOMEM; } + nfs_set_events(client); while (!task.complete) { - nfs_set_events(client); qemu_coroutine_yield(); } @@ -496,6 +492,22 @@ static int nfs_has_zero_init(BlockDriverState *bs) return client->has_zero_init; } +static void +nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, + void *private_data) +{ + NFSRPC *task = private_data; + task->ret = ret; + if (task->ret == 0) { + memcpy(task->st, data, sizeof(struct stat)); + } + if (task->ret < 0) { + error_report("NFS Error: %s", nfs_get_error(nfs)); + } + task->complete = 1; + bdrv_wakeup(task->bs); +} + static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) { NFSClient *client = bs->opaque; @@ -507,16 +519,15 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return client->st_blocks * 512; } + task.bs = bs; task.st = &st; - if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, + if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb, &task) != 0) { return -ENOMEM; } - while (!task.complete) { - nfs_set_events(client); - aio_poll(client->aio_context, true); - } + nfs_set_events(client); + BDRV_POLL_WHILE(bs, !task.complete); return (task.ret < 0 ? task.ret : st.st_blocks * 512); } diff --git a/block/qed-table.c b/block/qed-table.c index 1a731dff51..ed443e2b70 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -174,9 +174,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s) qed_read_table(s, s->header.l1_table_offset, s->l1_table, qed_sync_cb, &ret); - while (ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(s->bs), true); - } + BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS); return ret; } @@ -195,9 +193,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, int ret = -EINPROGRESS; qed_write_l1_table(s, index, n, qed_sync_cb, &ret); - while (ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(s->bs), true); - } + BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS); return ret; } @@ -268,9 +264,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset int ret = -EINPROGRESS; qed_read_l2_table(s, request, offset, qed_sync_cb, &ret); - while (ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(s->bs), true); - } + BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS); return ret; } @@ -290,9 +284,7 @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, int ret = -EINPROGRESS; qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret); - while (ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(s->bs), true); - } + BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS); return ret; } diff --git a/block/qed.c b/block/qed.c index 3ee879b52e..1a7ef0a9ce 100644 --- a/block/qed.c +++ b/block/qed.c @@ -336,7 +336,7 @@ static void qed_need_check_timer_cb(void *opaque) qed_plug_allocating_write_reqs(s); /* Ensure writes are on disk before clearing flag */ - bdrv_aio_flush(s->bs, qed_clear_need_check, s); + bdrv_aio_flush(s->bs->file->bs, qed_clear_need_check, s); } static void qed_start_need_check_timer(BDRVQEDState *s) @@ -378,6 +378,19 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs, } } +static void bdrv_qed_drain(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + + /* Fire the timer immediately in order to start doing I/O as soon as the + * header is flushed. + */ + if (s->need_check_timer && timer_pending(s->need_check_timer)) { + qed_cancel_need_check_timer(s); + qed_need_check_timer_cb(s); + } +} + static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -1668,6 +1681,7 @@ static BlockDriver bdrv_qed = { .bdrv_check = bdrv_qed_check, .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, + .bdrv_drain = bdrv_qed_drain, }; static void bdrv_qed_init(void) diff --git a/block/raw-posix.c b/block/raw-posix.c index f481e57f78..247e47b88f 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -2069,13 +2069,23 @@ static bool hdev_is_sg(BlockDriverState *bs) #if defined(__linux__) + BDRVRawState *s = bs->opaque; struct stat st; struct sg_scsi_id scsiid; int sg_version; + int ret; + + if (stat(bs->filename, &st) < 0 || !S_ISCHR(st.st_mode)) { + return false; + } - if (stat(bs->filename, &st) >= 0 && S_ISCHR(st.st_mode) && - !bdrv_ioctl(bs, SG_GET_VERSION_NUM, &sg_version) && - !bdrv_ioctl(bs, SG_GET_SCSI_ID, &scsiid)) { + ret = ioctl(s->fd, SG_GET_VERSION_NUM, &sg_version); + if (ret < 0) { + return false; + } + + ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); + if (ret >= 0) { DPRINTF("SG device found: type=%d, version=%d\n", scsiid.scsi_type, sg_version); return true; diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 588d4080f9..fc16ec1f74 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -176,12 +176,9 @@ static void raw_lock_medium(BlockDriverState *bs, bool locked) bdrv_lock_medium(bs->file->bs, locked); } -static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, - void *opaque) +static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { - return bdrv_aio_ioctl(bs->file->bs, req, buf, cb, opaque); + return bdrv_co_ioctl(bs->file->bs, req, buf); } static int raw_has_zero_init(BlockDriverState *bs) @@ -261,7 +258,7 @@ BlockDriver bdrv_raw = { .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, .bdrv_lock_medium = &raw_lock_medium, - .bdrv_aio_ioctl = &raw_aio_ioctl, + .bdrv_co_ioctl = &raw_co_ioctl, .create_opts = &raw_create_opts, .bdrv_has_zero_init = &raw_has_zero_init }; diff --git a/block/replication.c b/block/replication.c index 8bbfc8f870..02aeaaf7d0 100644 --- a/block/replication.c +++ b/block/replication.c @@ -138,6 +138,9 @@ static void replication_close(BlockDriverState *bs) if (s->replication_state == BLOCK_REPLICATION_RUNNING) { replication_stop(s->rs, false, NULL); } + if (s->replication_state == BLOCK_REPLICATION_FAILOVER) { + block_job_cancel_sync(s->active_disk->bs->job); + } if (s->mode == REPLICATION_MODE_SECONDARY) { g_free(s->top_id); @@ -319,9 +322,10 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) } } -static void reopen_backing_file(BDRVReplicationState *s, bool writable, +static void reopen_backing_file(BlockDriverState *bs, bool writable, Error **errp) { + BDRVReplicationState *s = bs->opaque; BlockReopenQueue *reopen_queue = NULL; int orig_hidden_flags, orig_secondary_flags; int new_hidden_flags, new_secondary_flags; @@ -356,13 +360,15 @@ static void reopen_backing_file(BDRVReplicationState *s, bool writable, } if (reopen_queue) { - bdrv_reopen_multiple(reopen_queue, &local_err); + bdrv_reopen_multiple(bdrv_get_aio_context(bs), + reopen_queue, &local_err); error_propagate(errp, local_err); } } -static void backup_job_cleanup(BDRVReplicationState *s) +static void backup_job_cleanup(BlockDriverState *bs) { + BDRVReplicationState *s = bs->opaque; BlockDriverState *top_bs; top_bs = bdrv_lookup_bs(s->top_id, s->top_id, NULL); @@ -371,19 +377,20 @@ static void backup_job_cleanup(BDRVReplicationState *s) } bdrv_op_unblock_all(top_bs, s->blocker); error_free(s->blocker); - reopen_backing_file(s, false, NULL); + reopen_backing_file(bs, false, NULL); } static void backup_job_completed(void *opaque, int ret) { - BDRVReplicationState *s = opaque; + BlockDriverState *bs = opaque; + BDRVReplicationState *s = bs->opaque; if (s->replication_state != BLOCK_REPLICATION_FAILOVER) { /* The backup job is cancelled unexpectedly */ s->error = -EIO; } - backup_job_cleanup(s); + backup_job_cleanup(bs); } static bool check_top_bs(BlockDriverState *top_bs, BlockDriverState *bs) @@ -479,7 +486,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, } /* reopen the backing file in r/w mode */ - reopen_backing_file(s, true, &local_err); + reopen_backing_file(bs, true, &local_err); if (local_err) { error_propagate(errp, local_err); aio_context_release(aio_context); @@ -494,7 +501,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (!top_bs || !bdrv_is_root_node(top_bs) || !check_top_bs(top_bs, bs)) { error_setg(errp, "No top_bs or it is invalid"); - reopen_backing_file(s, false, NULL); + reopen_backing_file(bs, false, NULL); aio_context_release(aio_context); return; } @@ -504,10 +511,10 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, backup_start("replication-backup", s->secondary_disk->bs, s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, - backup_job_completed, s, NULL, &local_err); + backup_job_completed, bs, NULL, &local_err); if (local_err) { error_propagate(errp, local_err); - backup_job_cleanup(s); + backup_job_cleanup(bs); aio_context_release(aio_context); return; } diff --git a/block/sheepdog.c b/block/sheepdog.c index ccbf7e1fa6..1fb917343a 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -641,6 +641,7 @@ static void restart_co_req(void *opaque) typedef struct SheepdogReqCo { int sockfd; + BlockDriverState *bs; AioContext *aio_context; SheepdogReq *hdr; void *data; @@ -701,6 +702,9 @@ out: srco->ret = ret; srco->finished = true; + if (srco->bs) { + bdrv_wakeup(srco->bs); + } } /* @@ -708,13 +712,14 @@ out: * * Return 0 on success, -errno in case of error. */ -static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr, +static int do_req(int sockfd, BlockDriverState *bs, SheepdogReq *hdr, void *data, unsigned int *wlen, unsigned int *rlen) { Coroutine *co; SheepdogReqCo srco = { .sockfd = sockfd, - .aio_context = aio_context, + .aio_context = bs ? bdrv_get_aio_context(bs) : qemu_get_aio_context(), + .bs = bs, .hdr = hdr, .data = data, .wlen = wlen, @@ -727,9 +732,14 @@ static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr, do_co_req(&srco); } else { co = qemu_coroutine_create(do_co_req, &srco); - qemu_coroutine_enter(co); - while (!srco.finished) { - aio_poll(aio_context, true); + if (bs) { + qemu_coroutine_enter(co); + BDRV_POLL_WHILE(bs, !srco.finished); + } else { + qemu_coroutine_enter(co); + while (!srco.finished) { + aio_poll(qemu_get_aio_context(), true); + } } } @@ -1125,7 +1135,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename, hdr.snapid = snapid; hdr.flags = SD_FLAG_CMD_WRITE; - ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + ret = do_req(fd, s->bs, (SheepdogReq *)&hdr, buf, &wlen, &rlen); if (ret) { error_setg_errno(errp, -ret, "cannot get vdi info"); goto out; @@ -1240,7 +1250,7 @@ out: qemu_co_mutex_unlock(&s->lock); } -static int read_write_object(int fd, AioContext *aio_context, char *buf, +static int read_write_object(int fd, BlockDriverState *bs, char *buf, uint64_t oid, uint8_t copies, unsigned int datalen, uint64_t offset, bool write, bool create, uint32_t cache_flags) @@ -1274,7 +1284,7 @@ static int read_write_object(int fd, AioContext *aio_context, char *buf, hdr.offset = offset; hdr.copies = copies; - ret = do_req(fd, aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + ret = do_req(fd, bs, (SheepdogReq *)&hdr, buf, &wlen, &rlen); if (ret) { error_report("failed to send a request to the sheep"); return ret; @@ -1289,22 +1299,22 @@ static int read_write_object(int fd, AioContext *aio_context, char *buf, } } -static int read_object(int fd, AioContext *aio_context, char *buf, +static int read_object(int fd, BlockDriverState *bs, char *buf, uint64_t oid, uint8_t copies, unsigned int datalen, uint64_t offset, uint32_t cache_flags) { - return read_write_object(fd, aio_context, buf, oid, copies, + return read_write_object(fd, bs, buf, oid, copies, datalen, offset, false, false, cache_flags); } -static int write_object(int fd, AioContext *aio_context, char *buf, +static int write_object(int fd, BlockDriverState *bs, char *buf, uint64_t oid, uint8_t copies, unsigned int datalen, uint64_t offset, bool create, uint32_t cache_flags) { - return read_write_object(fd, aio_context, buf, oid, copies, + return read_write_object(fd, bs, buf, oid, copies, datalen, offset, true, create, cache_flags); } @@ -1331,7 +1341,7 @@ static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag) goto out; } - ret = read_object(fd, s->aio_context, (char *)inode, vid_to_vdi_oid(vid), + ret = read_object(fd, s->bs, (char *)inode, vid_to_vdi_oid(vid), s->inode.nr_copies, SD_INODE_HEADER_SIZE, 0, s->cache_flags); if (ret < 0) { @@ -1489,7 +1499,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, } buf = g_malloc(SD_INODE_SIZE); - ret = read_object(fd, s->aio_context, buf, vid_to_vdi_oid(vid), + ret = read_object(fd, s->bs, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0, s->cache_flags); closesocket(fd); @@ -1618,7 +1628,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, hdr.copies = s->inode.nr_copies; hdr.block_size_shift = s->inode.block_size_shift; - ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + ret = do_req(fd, NULL, (SheepdogReq *)&hdr, buf, &wlen, &rlen); closesocket(fd); @@ -1886,7 +1896,7 @@ static int sd_create(const char *filename, QemuOpts *opts, hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT; hdr.proto_ver = SD_PROTO_VER; - ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + ret = do_req(fd, NULL, (SheepdogReq *)&hdr, NULL, &wlen, &rlen); closesocket(fd); if (ret) { @@ -1951,7 +1961,7 @@ static void sd_close(BlockDriverState *bs) hdr.data_length = wlen; hdr.flags = SD_FLAG_CMD_WRITE; - ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + ret = do_req(fd, s->bs, (SheepdogReq *)&hdr, s->name, &wlen, &rlen); closesocket(fd); @@ -2000,7 +2010,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) /* we don't need to update entire object */ datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); s->inode.vdi_size = offset; - ret = write_object(fd, s->aio_context, (char *)&s->inode, + ret = write_object(fd, s->bs, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, datalen, 0, false, s->cache_flags); close(fd); @@ -2070,7 +2080,7 @@ static bool sd_delete(BDRVSheepdogState *s) return false; } - ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + ret = do_req(fd, s->bs, (SheepdogReq *)&hdr, s->name, &wlen, &rlen); closesocket(fd); if (ret) { @@ -2126,7 +2136,7 @@ static int sd_create_branch(BDRVSheepdogState *s) goto out; } - ret = read_object(fd, s->aio_context, buf, vid_to_vdi_oid(vid), + ret = read_object(fd, s->bs, buf, vid_to_vdi_oid(vid), s->inode.nr_copies, SD_INODE_SIZE, 0, s->cache_flags); closesocket(fd); @@ -2411,7 +2421,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto cleanup; } - ret = write_object(fd, s->aio_context, (char *)&s->inode, + ret = write_object(fd, s->bs, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, datalen, 0, false, s->cache_flags); if (ret < 0) { @@ -2426,7 +2436,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto cleanup; } - ret = read_object(fd, s->aio_context, (char *)inode, + ret = read_object(fd, s->bs, (char *)inode, vid_to_vdi_oid(new_vid), s->inode.nr_copies, datalen, 0, s->cache_flags); @@ -2528,7 +2538,7 @@ static bool remove_objects(BDRVSheepdogState *s) i++; } - ret = write_object(fd, s->aio_context, + ret = write_object(fd, s->bs, (char *)&inode->data_vdi_id[start_idx], vid_to_vdi_oid(s->inode.vdi_id), inode->nr_copies, (i - start_idx) * sizeof(uint32_t), @@ -2600,7 +2610,7 @@ static int sd_snapshot_delete(BlockDriverState *bs, return -1; } - ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + ret = do_req(fd, s->bs, (SheepdogReq *)&hdr, buf, &wlen, &rlen); closesocket(fd); if (ret) { @@ -2652,8 +2662,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) req.opcode = SD_OP_READ_VDIS; req.data_length = max; - ret = do_req(fd, s->aio_context, &req, - vdi_inuse, &wlen, &rlen); + ret = do_req(fd, s->bs, &req, vdi_inuse, &wlen, &rlen); closesocket(fd); if (ret) { @@ -2679,7 +2688,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) } /* we don't need to read entire object */ - ret = read_object(fd, s->aio_context, (char *)&inode, + ret = read_object(fd, s->bs, (char *)&inode, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, s->cache_flags); @@ -2745,11 +2754,11 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, create = (offset == 0); if (load) { - ret = read_object(fd, s->aio_context, (char *)data, vmstate_oid, + ret = read_object(fd, s->bs, (char *)data, vmstate_oid, s->inode.nr_copies, data_len, offset, s->cache_flags); } else { - ret = write_object(fd, s->aio_context, (char *)data, vmstate_oid, + ret = write_object(fd, s->bs, (char *)data, vmstate_oid, s->inode.nr_copies, data_len, offset, create, s->cache_flags); } diff --git a/block/trace-events b/block/trace-events index 05fa13c891..aff8a9674d 100644 --- a/block/trace-events +++ b/block/trace-events @@ -9,7 +9,6 @@ blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags %x" # block/io.c -bdrv_aio_pdiscard(void *bs, int64_t offset, int count, void *opaque) "bs %p offset %"PRId64" count %d opaque %p" bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" diff --git a/blockdev-nbd.c b/blockdev-nbd.c index ca41cc6fdd..81bca1760f 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -44,6 +44,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, return TRUE; } + qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); nbd_client_new(NULL, cioc, nbd_server->tlscreds, NULL, nbd_client_put); @@ -111,6 +112,8 @@ void qmp_nbd_server_start(SocketAddress *addr, nbd_server = g_new0(NBDServerData, 1); nbd_server->watch = -1; nbd_server->listen_ioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc), + "nbd-listener"); if (qio_channel_socket_listen_sync( nbd_server->listen_ioc, addr, errp) < 0) { goto error; diff --git a/blockjob.c b/blockjob.c index 43fecbe13e..7c88b30074 100644 --- a/blockjob.c +++ b/blockjob.c @@ -74,17 +74,6 @@ BlockJob *block_job_get(const char *id) return NULL; } -/* Normally the job runs in its BlockBackend's AioContext. The exception is - * block_job_defer_to_main_loop() where it runs in the QEMU main loop. Code - * that supports both cases uses this helper function. - */ -static AioContext *block_job_get_aio_context(BlockJob *job) -{ - return job->deferred_to_main_loop ? - qemu_get_aio_context() : - blk_get_aio_context(job->blk); -} - static void block_job_attached_aio_context(AioContext *new_context, void *opaque) { @@ -97,6 +86,17 @@ static void block_job_attached_aio_context(AioContext *new_context, block_job_resume(job); } +static void block_job_drain(BlockJob *job) +{ + /* If job is !job->busy this kicks it into the next pause point. */ + block_job_enter(job); + + blk_drain(job->blk); + if (job->driver->drain) { + job->driver->drain(job); + } +} + static void block_job_detach_aio_context(void *opaque) { BlockJob *job = opaque; @@ -106,12 +106,8 @@ static void block_job_detach_aio_context(void *opaque) block_job_pause(job); - if (!job->paused) { - /* If job is !job->busy this kicks it into the next pause point. */ - block_job_enter(job); - } while (!job->paused && !job->completed) { - aio_poll(block_job_get_aio_context(job), true); + block_job_drain(job); } block_job_unref(job); @@ -413,14 +409,21 @@ static int block_job_finish_sync(BlockJob *job, assert(blk_bs(job->blk)->job == job); block_job_ref(job); + finish(job, &local_err); if (local_err) { error_propagate(errp, local_err); block_job_unref(job); return -EBUSY; } + /* block_job_drain calls block_job_enter, and it should be enough to + * induce progress until the job completes or moves to the main thread. + */ + while (!job->deferred_to_main_loop && !job->completed) { + block_job_drain(job); + } while (!job->completed) { - aio_poll(block_job_get_aio_context(job), true); + aio_poll(qemu_get_aio_context(), true); } ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; block_job_unref(job); @@ -230,6 +230,7 @@ vhost_net="no" vhost_scsi="no" vhost_vsock="no" kvm="no" +colo="yes" rdma="" gprof="no" debug_tcg="no" @@ -918,6 +919,10 @@ for opt do ;; --enable-kvm) kvm="yes" ;; + --disable-colo) colo="no" + ;; + --enable-colo) colo="yes" + ;; --disable-tcg-interpreter) tcg_interpreter="no" ;; --enable-tcg-interpreter) tcg_interpreter="yes" @@ -1366,6 +1371,7 @@ disabled with --disable-FEATURE, default is enabled if available: fdt fdt device tree bluez bluez stack connectivity kvm KVM acceleration support + colo COarse-grain LOck-stepping VM for Non-stop Service rdma RDMA-based migration support vde support for vde network netmap support for netmap network @@ -2917,27 +2923,38 @@ fi # curses probe if test "$curses" != "no" ; then if test "$mingw32" = "yes" ; then - curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses" + curses_inc_list="$($pkg_config --cflags ncurses 2>/dev/null):" + curses_lib_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses" else - curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lncurses:-lcurses" + curses_inc_list="$($pkg_config --cflags ncursesw 2>/dev/null):" + curses_lib_list="$($pkg_config --libs ncursesw 2>/dev/null):-lncursesw:-lcursesw" fi curses_found=no cat > $TMPC << EOF +#include <locale.h> #include <curses.h> +#include <wchar.h> int main(void) { const char *s = curses_version(); + wchar_t wch = L'w'; + setlocale(LC_ALL, ""); resize_term(0, 0); + addwstr(L"wide chars\n"); + addnwstr(&wch, 1); return s != 0; } EOF IFS=: - for curses_lib in $curses_list; do - unset IFS - if compile_prog "" "$curses_lib" ; then - curses_found=yes - libs_softmmu="$curses_lib $libs_softmmu" - break - fi + for curses_inc in $curses_inc_list; do + for curses_lib in $curses_lib_list; do + unset IFS + if compile_prog "$curses_inc" "$curses_lib" ; then + curses_found=yes + QEMU_CFLAGS="$curses_inc $QEMU_CFLAGS" + libs_softmmu="$curses_lib $libs_softmmu" + break + fi + done done unset IFS if test "$curses_found" = "yes" ; then @@ -4993,6 +5010,7 @@ echo "Linux AIO support $linux_aio" echo "ATTR/XATTR support $attr" echo "Install blobs $blobs" echo "KVM support $kvm" +echo "COLO support $colo" echo "RDMA support $rdma" echo "TCG interpreter $tcg_interpreter" echo "fdt support $fdt" @@ -5628,6 +5646,10 @@ if have_backend "syslog"; then fi echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak +if test "$colo" = "yes"; then + echo "CONFIG_COLO=y" >> $config_host_mak +fi + if test "$rdma" = "yes" ; then echo "CONFIG_RDMA=y" >> $config_host_mak fi @@ -6131,6 +6153,7 @@ FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" FILES="$FILES pc-bios/qemu-icon.bmp" for bios_file in \ $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.lid \ $source_path/pc-bios/*.aml \ $source_path/pc-bios/*.rom \ $source_path/pc-bios/*.dtb \ diff --git a/cpu-exec.c b/cpu-exec.c index e9b50a6b30..3e408865a8 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -152,12 +152,6 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) && qemu_log_in_addr_range(itb->pc)) { #if defined(TARGET_I386) log_cpu_state(cpu, CPU_DUMP_CCOP); -#elif defined(TARGET_M68K) - /* ??? Should not modify env state for dumping. */ - cpu_m68k_flush_flags(env, env->cc_op); - env->cc_op = CC_OP_FLAGS; - env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); - log_cpu_state(cpu, 0); #else log_cpu_state(cpu, 0); #endif @@ -26,7 +26,6 @@ #include "exec/cputlb.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/error-report.h" #include "exec/log.h" diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index db5a4d6f5e..67a9bcaa67 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -39,6 +39,7 @@ CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_PSERIES=y +CONFIG_POWERNV=y CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y diff --git a/dma-helpers.c b/dma-helpers.c index 9defc101b7..6f9d47ca50 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -73,6 +73,7 @@ typedef struct { AioContext *ctx; BlockAIOCB *acb; QEMUSGList *sg; + uint32_t align; uint64_t offset; DMADirection dir; int sg_cur_index; @@ -160,8 +161,9 @@ static void dma_blk_cb(void *opaque, int ret) return; } - if (dbs->iov.size & ~BDRV_SECTOR_MASK) { - qemu_iovec_discard_back(&dbs->iov, dbs->iov.size & ~BDRV_SECTOR_MASK); + if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) { + qemu_iovec_discard_back(&dbs->iov, + QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align)); } dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, @@ -199,7 +201,7 @@ static const AIOCBInfo dma_aiocb_info = { }; BlockAIOCB *dma_blk_io(AioContext *ctx, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, void *opaque, DMADirection dir) @@ -212,6 +214,7 @@ BlockAIOCB *dma_blk_io(AioContext *ctx, dbs->sg = sg; dbs->ctx = ctx; dbs->offset = offset; + dbs->align = align; dbs->sg_cur_index = 0; dbs->sg_cur_byte = 0; dbs->dir = dir; @@ -234,11 +237,11 @@ BlockAIOCB *dma_blk_read_io_func(int64_t offset, QEMUIOVector *iov, } BlockAIOCB *dma_blk_read(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), - sg, offset, dma_blk_read_io_func, blk, cb, opaque, + return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + dma_blk_read_io_func, blk, cb, opaque, DMA_DIRECTION_FROM_DEVICE); } @@ -252,11 +255,11 @@ BlockAIOCB *dma_blk_write_io_func(int64_t offset, QEMUIOVector *iov, } BlockAIOCB *dma_blk_write(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), - sg, offset, dma_blk_write_io_func, blk, cb, opaque, + return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + dma_blk_write_io_func, blk, cb, opaque, DMA_DIRECTION_TO_DEVICE); } diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt new file mode 100644 index 0000000000..628293824c --- /dev/null +++ b/docs/COLO-FT.txt @@ -0,0 +1,189 @@ +COarse-grained LOck-stepping Virtual Machines for Non-stop Service +---------------------------------------- +Copyright (c) 2016 Intel Corporation +Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. +Copyright (c) 2016 Fujitsu, Corp. + +This work is licensed under the terms of the GNU GPL, version 2 or later. +See the COPYING file in the top-level directory. + +This document gives an overview of COLO's design and how to use it. + +== Background == +Virtual machine (VM) replication is a well known technique for providing +application-agnostic software-implemented hardware fault tolerance, +also known as "non-stop service". + +COLO (COarse-grained LOck-stepping) is a high availability solution. +Both primary VM (PVM) and secondary VM (SVM) run in parallel. They receive the +same request from client, and generate response in parallel too. +If the response packets from PVM and SVM are identical, they are released +immediately. Otherwise, a VM checkpoint (on demand) is conducted. + +== Architecture == + +The architecture of COLO is shown in the diagram below. +It consists of a pair of networked physical nodes: +The primary node running the PVM, and the secondary node running the SVM +to maintain a valid replica of the PVM. +PVM and SVM execute in parallel and generate output of response packets for +client requests according to the application semantics. + +The incoming packets from the client or external network are received by the +primary node, and then forwarded to the secondary node, so that both the PVM +and the SVM are stimulated with the same requests. + +COLO receives the outbound packets from both the PVM and SVM and compares them +before allowing the output to be sent to clients. + +The SVM is qualified as a valid replica of the PVM, as long as it generates +identical responses to all client requests. Once the differences in the outputs +are detected between the PVM and SVM, COLO withholds transmission of the +outbound packets until it has successfully synchronized the PVM state to the SVM. + + Primary Node Secondary Node + +------------+ +-----------------------+ +------------------------+ +------------+ + | | | HeartBeat |<----->| HeartBeat | | | + | Primary VM | +-----------|-----------+ +-----------|------------+ |Secondary VM| + | | | | | | + | | +-----------|-----------+ +-----------|------------+ | | + | | |QEMU +---v----+ | |QEMU +----v---+ | | | + | | | |Failover| | | |Failover| | | | + | | | +--------+ | | +--------+ | | | + | | | +---------------+ | | +---------------+ | | | + | | | | VM Checkpoint |-------------->| VM Checkpoint | | | | + | | | +---------------+ | | +---------------+ | | | + | | | | | | | | + |Requests<---------------------------^------------------------------------------>Requests| + |Responses----------------------\ /--|--------------\ /------------------------Responses| + | | | | | | | | | | | | | + | | | +-----------+ | | | | | | | +------------+ | | | + | | | | COLO disk | | | | | | | | | COLO disk | | | | + | | | | Manager |-|-|--|--------------|--|->| Manager | | | | + | | | +|----------+ | | | | | | | +-----------|+ | | | + | | | | | | | | | | | | | | | + +------------+ +--|------------|-|--|--+ +---|--|--------------|--+ +------------+ + | | | | | | | + +-------------+ | +----------v-v--|--+ +---|--v-----------+ | +-------------+ + | VM Monitor | | | COLO Proxy | | COLO Proxy | | | VM Monitor | + | | | |(compare packet) | | (adjust sequence)| | | | + +-------------+ | +----------|----^--+ +------------------+ | +-------------+ + | | | | + +------------------|------------|----|--+ +---------------------|------------------+ + | Kernel | | | | | Kernel | | + +------------------|------------|----|--+ +---------------------|------------------+ + | | | | + +--------------v+ +--------v----|--+ +------------------+ +v-------------+ + | Storage | |External Network| | External Network | | Storage | + +---------------+ +----------------+ +------------------+ +--------------+ + +== Components introduction == + +You can see there are several components in COLO's diagram of architecture. +Their functions are described below. + +HeartBeat: +Runs on both the primary and secondary nodes, to periodically check platform +availability. When the primary node suffers a hardware fail-stop failure, +the heartbeat stops responding, the secondary node will trigger a failover +as soon as it determines the absence. + +COLO disk Manager: +When primary VM writes data into image, the colo disk manger captures this data +and sends it to secondary VM's which makes sure the context of secondary VM's +image is consistent with the context of primary VM 's image. +For more details, please refer to docs/block-replication.txt. + +Checkpoint/Failover Controller: +Modifications of save/restore flow to realize continuous migration, +to make sure the state of VM in Secondary side is always consistent with VM in +Primary side. + +COLO Proxy: +Delivers packets to Primary and Seconday, and then compare the responses from +both side. Then decide whether to start a checkpoint according to some rules. +Please refer to docs/colo-proxy.txt for more informations. + +Note: +HeartBeat has not been implemented yet, so you need to trigger failover process +by using 'x-colo-lost-heartbeat' command. + +== Test procedure == +1. Startup qemu +Primary: +# qemu-kvm -enable-kvm -m 2048 -smp 2 -qmp stdio -vnc :7 -name primary \ + -device piix3-usb-uhci \ + -device usb-tablet -netdev tap,id=hn0,vhost=off \ + -device virtio-net-pci,id=net-pci0,netdev=hn0 \ + -drive if=virtio,id=primary-disk0,driver=quorum,read-pattern=fifo,vote-threshold=1,\ + children.0.file.filename=1.raw,\ + children.0.driver=raw -S +Secondary: +# qemu-kvm -enable-kvm -m 2048 -smp 2 -qmp stdio -vnc :7 -name secondary \ + -device piix3-usb-uhci \ + -device usb-tablet -netdev tap,id=hn0,vhost=off \ + -device virtio-net-pci,id=net-pci0,netdev=hn0 \ + -drive if=none,id=secondary-disk0,file.filename=1.raw,driver=raw,node-name=node0 \ + -drive if=virtio,id=active-disk0,driver=replication,mode=secondary,\ + file.driver=qcow2,top-id=active-disk0,\ + file.file.filename=/mnt/ramfs/active_disk.img,\ + file.backing.driver=qcow2,\ + file.backing.file.filename=/mnt/ramfs/hidden_disk.img,\ + file.backing.backing=secondary-disk0 \ + -incoming tcp:0:8888 + +2. On Secondary VM's QEMU monitor, issue command +{'execute':'qmp_capabilities'} +{ 'execute': 'nbd-server-start', + 'arguments': {'addr': {'type': 'inet', 'data': {'host': 'xx.xx.xx.xx', 'port': '8889'} } } +} +{'execute': 'nbd-server-add', 'arguments': {'device': 'secondeary-disk0', 'writable': true } } + +Note: + a. The qmp command nbd-server-start and nbd-server-add must be run + before running the qmp command migrate on primary QEMU + b. Active disk, hidden disk and nbd target's length should be the + same. + c. It is better to put active disk and hidden disk in ramdisk. + +3. On Primary VM's QEMU monitor, issue command: +{'execute':'qmp_capabilities'} +{ 'execute': 'human-monitor-command', + 'arguments': {'command-line': 'drive_add -n buddy driver=replication,mode=primary,file.driver=nbd,file.host=xx.xx.xx.xx,file.port=8889,file.export=secondary-disk0,node-name=nbd_client0'}} +{ 'execute':'x-blockdev-change', 'arguments':{'parent': 'primary-disk0', 'node': 'nbd_client0' } } +{ 'execute': 'migrate-set-capabilities', + 'arguments': {'capabilities': [ {'capability': 'x-colo', 'state': true } ] } } +{ 'execute': 'migrate', 'arguments': {'uri': 'tcp:xx.xx.xx.xx:8888' } } + + Note: + a. There should be only one NBD Client for each primary disk. + b. xx.xx.xx.xx is the secondary physical machine's hostname or IP + c. The qmp command line must be run after running qmp command line in + secondary qemu. + +4. After the above steps, you will see, whenever you make changes to PVM, SVM will be synced. +You can issue command '{ "execute": "migrate-set-parameters" , "arguments":{ "x-checkpoint-delay": 2000 } }' +to change the checkpoint period time + +5. Failover test +You can kill Primary VM and run 'x_colo_lost_heartbeat' in Secondary VM's +monitor at the same time, then SVM will failover and client will not detect this +change. + +Before issuing '{ "execute": "x-colo-lost-heartbeat" }' command, we have to +issue block related command to stop block replication. +Primary: + Remove the nbd child from the quorum: + { 'execute': 'x-blockdev-change', 'arguments': {'parent': 'colo-disk0', 'child': 'children.1'}} + { 'execute': 'human-monitor-command','arguments': {'command-line': 'drive_del blk-buddy0'}} + Note: there is no qmp command to remove the blockdev now + +Secondary: + The primary host is down, so we should do the following thing: + { 'execute': 'nbd-server-stop' } + +== TODO == +1. Support continuous VM replication. +2. Support shared storage. +3. Develop the heartbeat part. +4. Reduce checkpoint VM’s downtime while doing checkpoint. diff --git a/docs/multiple-iothreads.txt b/docs/multiple-iothreads.txt index 40b8419916..0e7cdb2c28 100644 --- a/docs/multiple-iothreads.txt +++ b/docs/multiple-iothreads.txt @@ -105,13 +105,10 @@ a BH in the target AioContext beforehand and then call qemu_bh_schedule(). No acquire/release or locking is needed for the qemu_bh_schedule() call. But be sure to acquire the AioContext for aio_bh_new() if necessary. -The relationship between AioContext and the block layer -------------------------------------------------------- -The AioContext originates from the QEMU block layer because it provides a -scoped way of running event loop iterations until all work is done. This -feature is used to complete all in-flight block I/O requests (see -bdrv_drain_all()). Nowadays AioContext is a generic event loop that can be -used by any QEMU subsystem. +AioContext and the block layer +------------------------------ +The AioContext originates from the QEMU block layer, even though nowadays +AioContext is a generic event loop that can be used by any QEMU subsystem. The block layer has support for AioContext integrated. Each BlockDriverState is associated with an AioContext using bdrv_set_aio_context() and @@ -122,13 +119,22 @@ Block layer code must therefore expect to run in an IOThread and avoid using old APIs that implicitly use the main loop. See the "How to program for IOThreads" above for information on how to do that. -If main loop code such as a QMP function wishes to access a BlockDriverState it -must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure the -IOThread does not run in parallel. - -Long-running jobs (usually in the form of coroutines) are best scheduled in the -BlockDriverState's AioContext to avoid the need to acquire/release around each -bdrv_*() call. Be aware that there is currently no mechanism to get notified -when bdrv_set_aio_context() moves this BlockDriverState to a different -AioContext (see bdrv_detach_aio_context()/bdrv_attach_aio_context()), so you -may need to add this if you want to support long-running jobs. +If main loop code such as a QMP function wishes to access a BlockDriverState +it must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure +that callbacks in the IOThread do not run in parallel. + +Code running in the monitor typically needs to ensure that past +requests from the guest are completed. When a block device is running +in an IOThread, the IOThread can also process requests from the guest +(via ioeventfd). To achieve both objects, wrap the code between +bdrv_drained_begin() and bdrv_drained_end(), thus creating a "drained +section". The functions must be called between aio_context_acquire() +and aio_context_release(). You can freely release and re-acquire the +AioContext within a drained section. + +Long-running jobs (usually in the form of coroutines) are best scheduled in +the BlockDriverState's AioContext to avoid the need to acquire/release around +each bdrv_*() call. The functions bdrv_add/remove_aio_context_notifier, +or alternatively blk_add/remove_aio_context_notifier if you use BlockBackends, +can be used to get a notification whenever bdrv_set_aio_context() moves a +BlockDriverState to a different AioContext. diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt index 284576d795..a4732a570c 100644 --- a/docs/qmp-commands.txt +++ b/docs/qmp-commands.txt @@ -554,6 +554,16 @@ Example: -> { "execute": "migrate_set_downtime", "arguments": { "value": 0.1 } } <- { "return": {} } +x-colo-lost-heartbeat +-------------------- + +Tell COLO that heartbeat is lost, a failover or takeover is needed. + +Example: + +-> { "execute": "x-colo-lost-heartbeat" } +<- { "return": {} } + client_migrate_info ------------------- @@ -2861,6 +2871,7 @@ Enable/Disable migration capabilities - "compress": use multiple compression threads to accelerate live migration - "events": generate events for each migration state change - "postcopy-ram": postcopy mode for live migration +- "x-colo": COarse-Grain LOck Stepping (COLO) for Non-stop Service Arguments: @@ -2882,6 +2893,7 @@ Query current migration capabilities - "compress": Multiple compression threads state (json-bool) - "events": Migration state change event state (json-bool) - "postcopy-ram": postcopy ram state (json-bool) + - "x-colo": COarse-Grain LOck Stepping for Non-stop Service (json-bool) Arguments: @@ -2895,7 +2907,8 @@ Example: {"state": false, "capability": "zero-blocks"}, {"state": false, "capability": "compress"}, {"state": true, "capability": "events"}, - {"state": false, "capability": "postcopy-ram"} + {"state": false, "capability": "postcopy-ram"}, + {"state": false, "capability": "x-colo"} ]} migrate-set-parameters @@ -2913,6 +2926,8 @@ Set migration parameters - "max-bandwidth": set maximum speed for migrations (in bytes/sec) (json-int) - "downtime-limit": set maximum tolerated downtime (in milliseconds) for migrations (json-int) +- "x-checkpoint-delay": set the delay time for periodic checkpoint (json-int) + Arguments: Example: diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt index 631b0cadae..f57e2a09c6 100644 --- a/docs/specs/ppc-spapr-hotplug.txt +++ b/docs/specs/ppc-spapr-hotplug.txt @@ -233,12 +233,27 @@ tools by host-level management such as an HMC. This level of management is not applicable to PowerKVM, hence the reason for extending the notification framework to support hotplug events. -Note that these events are not yet formally part of the PAPR+ specification, -but support for this format has already been implemented in DR-related -guest tools such as powerpc-utils/librtas, as well as kernel patches that have -been submitted to handle in-kernel processing of memory/cpu-related hotplug -events[1], and is planned for formal inclusion is PAPR+ specification. The -hotplug-specific payload is QEMU implemented as follows (with all values +The format for these EPOW-signalled events is described below under +"hotplug/unplug event structure". Note that these events are not +formally part of the PAPR+ specification, and have been superseded by a +newer format, also described below under "hotplug/unplug event structure", +and so are now deemed a "legacy" format. The formats are similar, but the +"modern" format contains additional fields/flags, which are denoted for the +purposes of this documentation with "#ifdef GUEST_SUPPORTS_MODERN" guards. + +QEMU should assume support only for "legacy" fields/flags unless the guest +advertises support for the "modern" format via ibm,client-architecture-support +hcall by setting byte 5, bit 6 of it's ibm,architecture-vec-5 option vector +structure (as described by LoPAPR v11, B.6.2.3). As with "legacy" format events, +"modern" format events are surfaced to the guest via check-exception RTAS calls, +but use a dedicated event source to signal the guest. This event source is +advertised to the guest by the addition of a "hot-plug-events" node under +"/event-sources" node of the guest's device tree using the standard format +described in LoPAPR v11, B.6.12.1. + +== hotplug/unplug event structure == + +The hotplug-specific payload in QEMU is implemented as follows (with all values encoded in big-endian format): struct rtas_event_log_v6_hp { @@ -263,14 +278,23 @@ struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_HP_ACTION_ADD 1 #define RTAS_LOG_V6_HP_ACTION_REMOVE 2 uint8_t hotplug_action; /* action (add/remove) */ -#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 -#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 -#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 +#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 +#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 +#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 +#ifdef GUEST_SUPPORTS_MODERN +#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4 +#endif uint8_t hotplug_identifier; /* type of the resource identifier, * which serves as the discriminator * for the 'drc' union field below */ +#ifdef GUEST_SUPPORTS_MODERN + uint8_t capabilities; /* capability flags, currently unused + * by QEMU + */ +#else uint8_t reserved; +#endif union { uint32_t index; /* DRC index of resource to take action * on @@ -278,6 +302,19 @@ struct rtas_event_log_v6_hp { uint32_t count; /* number of DR resources to take * action on (guest chooses which) */ +#ifdef GUEST_SUPPORTS_MODERN + struct { + uint32_t count; /* number of DR resources to take + * action on + */ + uint32_t index; /* DRC index of first resource to take + * action on. guest will take action + * on DRC index <index> through + * DRC index <index + count - 1> in + * sequential order + */ + } count_indexed; +#endif char name[1]; /* string representing the name of the * DRC to take action on */ @@ -31,7 +31,6 @@ #define MAX_PACKET_LENGTH 4096 -#include "cpu.h" #include "qemu/sockets.h" #include "sysemu/kvm.h" #include "exec/semihost.h" diff --git a/hmp-commands.hx b/hmp-commands.hx index 06bef470b9..88192817b2 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1040,6 +1040,21 @@ migration (or once already in postcopy). ETEXI { + .name = "x_colo_lost_heartbeat", + .args_type = "", + .params = "", + .help = "Tell COLO that heartbeat is lost,\n\t\t\t" + "a failover or takeover is needed.", + .cmd = hmp_x_colo_lost_heartbeat, + }, + +STEXI +@item x_colo_lost_heartbeat +@findex x_colo_lost_heartbeat +Tell COLO that heartbeat is lost, a failover or takeover is needed. +ETEXI + + { .name = "client_migrate_info", .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", @@ -318,6 +318,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, " %s: %" PRId64 " milliseconds", MigrationParameter_lookup[MIGRATION_PARAMETER_DOWNTIME_LIMIT], params->downtime_limit); + monitor_printf(mon, " %s: %" PRId64, + MigrationParameter_lookup[MIGRATION_PARAMETER_X_CHECKPOINT_DELAY], + params->x_checkpoint_delay); monitor_printf(mon, "\n"); } @@ -1386,6 +1389,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p.has_downtime_limit = true; use_int_value = true; break; + case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY: + p.has_x_checkpoint_delay = true; + use_int_value = true; + break; } if (use_int_value) { @@ -1402,6 +1409,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p.cpu_throttle_initial = valueint; p.cpu_throttle_increment = valueint; p.downtime_limit = valueint; + p.x_checkpoint_delay = valueint; } qmp_migrate_set_parameters(&p, &err); @@ -1443,6 +1451,14 @@ void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_x_colo_lost_heartbeat(&err); + hmp_handle_error(mon, &err); +} + void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); @@ -72,6 +72,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict); void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict); void hmp_client_migrate_info(Monitor *mon, const QDict *qdict); void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict); +void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); void hmp_eject(Monitor *mon, const QDict *qdict); diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index fbd78ed01c..dd19ba3c99 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -74,6 +74,7 @@ static void cubieboard_init(MachineState *machine) cubieboard_binfo.ram_size = machine->ram_size; cubieboard_binfo.kernel_filename = machine->kernel_filename; cubieboard_binfo.kernel_cmdline = machine->kernel_cmdline; + cubieboard_binfo.initrd_filename = machine->initrd_filename; arm_load_kernel(&s->a10->cpu, &cubieboard_binfo); } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 42cdde0478..21ea1d6210 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -2267,7 +2267,9 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); s->cm_base = 0x41300000; - s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ + s->cm_regs[CCCR >> 2] = 0x00000121; /* from datasheet */ + s->cm_regs[CKEN >> 2] = 0x00017def; /* from datasheet */ + s->clkcfg = 0x00000009; /* Turbo mode active */ memory_region_init_io(&s->cm_iomem, NULL, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 41cc2eeeb1..949a15ae64 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -29,6 +29,7 @@ #include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" +#include "sysemu/sysemu.h" #undef REG_FMT #define REG_FMT "0x%02lx" @@ -844,9 +845,18 @@ static void spitz_lcd_hsync_handler(void *opaque, int line, int level) spitz_hsync ^= 1; } +static void spitz_reset(void *opaque, int line, int level) +{ + if (level) { + qemu_system_reset_request(); + } +} + static void spitz_gpio_setup(PXA2xxState *cpu, int slots) { qemu_irq lcd_hsync; + qemu_irq reset; + /* * Bad hack: We toggle the LCD hsync GPIO on every GPIO status * read to satisfy broken guests that poll-wait for hsync. @@ -867,7 +877,8 @@ static void spitz_gpio_setup(PXA2xxState *cpu, int slots) qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER)); /* Handle reset */ - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset); + reset = qemu_allocate_irq(spitz_reset, cpu, 0); + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, reset); /* PCMCIA signals: card's IRQ and Card-Detect */ if (slots >= 1) diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 2db66508b5..1ee12f49b3 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -25,6 +25,7 @@ #include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" +#include "sysemu/sysemu.h" #define TOSA_RAM 0x04000000 #define TOSA_ROM 0x00800000 @@ -86,6 +87,12 @@ static void tosa_out_switch(void *opaque, int line, int level) } } +static void tosa_reset(void *opaque, int line, int level) +{ + if (level) { + qemu_system_reset_request(); + } +} static void tosa_gpio_setup(PXA2xxState *cpu, DeviceState *scp0, @@ -93,13 +100,16 @@ static void tosa_gpio_setup(PXA2xxState *cpu, TC6393xbState *tmio) { qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4); + qemu_irq reset; + /* MMC/SD host */ pxa2xx_mmci_handlers(cpu->mmc, qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP), qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT))); /* Handle reset */ - qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset); + reset = qemu_allocate_irq(tosa_reset, cpu, 0); + qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, reset); /* PCMCIA signals: card's IRQ and Card-Detect */ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 8ae5392bcc..7b5cb36d5a 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -198,6 +198,15 @@ static void versatile_init(MachineState *machine, int board_id) int done_smc = 0; DriveInfo *dinfo; + if (machine->ram_size > 0x10000000) { + /* Device starting at address 0x10000000, + * and memory cannot overlap with devices. + * Refuse to run rather than behaving very confusingly. + */ + error_report("versatilepb: memory size must not exceed 256MB"); + exit(1); + } + if (!machine->cpu_model) { machine->cpu_model = "arm926"; } diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 5fc10df08e..f953610018 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -594,7 +594,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) gicc->uid = i; gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); - if (armcpu->has_pmu) { + if (arm_feature(&armcpu->env, ARM_FEATURE_PMU)) { gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ)); } } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 070bbf89d4..54a8b28a58 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -85,6 +85,7 @@ typedef struct { VirtBoardInfo *daughterboard; bool disallow_affinity_adjustment; bool no_its; + bool no_pmu; } VirtMachineClass; typedef struct { @@ -490,7 +491,7 @@ static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi, int gictype) CPU_FOREACH(cpu) { armcpu = ARM_CPU(cpu); - if (!armcpu->has_pmu || + if (!arm_feature(&armcpu->env, ARM_FEATURE_PMU) || !kvm_arm_pmu_create(cpu, PPI(VIRTUAL_PMU_IRQ))) { return; } @@ -1353,6 +1354,10 @@ static void machvirt_init(MachineState *machine) } } + if (vmc->no_pmu && object_property_find(cpuobj, "pmu", NULL)) { + object_property_set_bool(cpuobj, false, "pmu", NULL); + } + if (object_property_find(cpuobj, "reset-cbar", NULL)) { object_property_set_int(cpuobj, vbi->memmap[VIRT_CPUPERIPHS].base, "reset-cbar", &error_abort); @@ -1592,5 +1597,7 @@ static void virt_machine_2_6_options(MachineClass *mc) virt_machine_2_7_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_6); vmc->disallow_affinity_adjustment = true; + /* Disable PMU for 2.6 as PMU support was first introduced in 2.7 */ + vmc->no_pmu = true; } DEFINE_VIRT_MACHINE(2, 6) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index cd95340cd9..537face94d 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -416,7 +416,8 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } left = len; - while (left > 0) { + s = st->bentries; + while (left > 0 && s-- > 0) { copy = left; if (copy > st->bsize - st->lpib) copy = st->bsize - st->lpib; diff --git a/hw/block/fdc.c b/hw/block/fdc.c index b79873af27..17d29e7bc5 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -35,6 +35,7 @@ #include "qemu/timer.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" +#include "hw/block/block.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" @@ -52,6 +53,35 @@ } \ } while (0) + +/********************************************************/ +/* qdev floppy bus */ + +#define TYPE_FLOPPY_BUS "floppy-bus" +#define FLOPPY_BUS(obj) OBJECT_CHECK(FloppyBus, (obj), TYPE_FLOPPY_BUS) + +typedef struct FDCtrl FDCtrl; +typedef struct FDrive FDrive; +static FDrive *get_drv(FDCtrl *fdctrl, int unit); + +typedef struct FloppyBus { + BusState bus; + FDCtrl *fdc; +} FloppyBus; + +static const TypeInfo floppy_bus_info = { + .name = TYPE_FLOPPY_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(FloppyBus), +}; + +static void floppy_bus_create(FDCtrl *fdc, FloppyBus *bus, DeviceState *dev) +{ + qbus_create_inplace(bus, sizeof(FloppyBus), TYPE_FLOPPY_BUS, dev, NULL); + bus->fdc = fdc; +} + + /********************************************************/ /* Floppy drive emulation */ @@ -148,14 +178,12 @@ static FDriveSize drive_size(FloppyDriveType drive) #define FD_SECTOR_SC 2 /* Sector size code */ #define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ -typedef struct FDCtrl FDCtrl; - /* Floppy disk drive emulation */ typedef enum FDiskFlags { FDISK_DBL_SIDES = 0x01, } FDiskFlags; -typedef struct FDrive { +struct FDrive { FDCtrl *fdctrl; BlockBackend *blk; /* Drive status */ @@ -176,7 +204,7 @@ typedef struct FDrive { uint8_t media_rate; /* Data rate of medium */ bool media_validated; /* Have we validated the media? */ -} FDrive; +}; static FloppyDriveType get_fallback_drive_type(FDrive *drv); @@ -441,6 +469,135 @@ static void fd_revalidate(FDrive *drv) } } +static void fd_change_cb(void *opaque, bool load) +{ + FDrive *drive = opaque; + + drive->media_changed = 1; + drive->media_validated = false; + fd_revalidate(drive); +} + +static const BlockDevOps fd_block_ops = { + .change_media_cb = fd_change_cb, +}; + + +#define TYPE_FLOPPY_DRIVE "floppy" +#define FLOPPY_DRIVE(obj) \ + OBJECT_CHECK(FloppyDrive, (obj), TYPE_FLOPPY_DRIVE) + +typedef struct FloppyDrive { + DeviceState qdev; + uint32_t unit; + BlockConf conf; + FloppyDriveType type; +} FloppyDrive; + +static Property floppy_drive_properties[] = { + DEFINE_PROP_UINT32("unit", FloppyDrive, unit, -1), + DEFINE_BLOCK_PROPERTIES(FloppyDrive, conf), + DEFINE_PROP_DEFAULT("drive-type", FloppyDrive, type, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_END_OF_LIST(), +}; + +static int floppy_drive_init(DeviceState *qdev) +{ + FloppyDrive *dev = FLOPPY_DRIVE(qdev); + FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus); + FDrive *drive; + int ret; + + if (dev->unit == -1) { + for (dev->unit = 0; dev->unit < MAX_FD; dev->unit++) { + drive = get_drv(bus->fdc, dev->unit); + if (!drive->blk) { + break; + } + } + } + + if (dev->unit >= MAX_FD) { + error_report("Can't create floppy unit %d, bus supports only %d units", + dev->unit, MAX_FD); + return -1; + } + + drive = get_drv(bus->fdc, dev->unit); + if (drive->blk) { + error_report("Floppy unit %d is in use", dev->unit); + return -1; + } + + if (!dev->conf.blk) { + /* Anonymous BlockBackend for an empty drive */ + dev->conf.blk = blk_new(); + ret = blk_attach_dev(dev->conf.blk, qdev); + assert(ret == 0); + } + + blkconf_blocksizes(&dev->conf); + if (dev->conf.logical_block_size != 512 || + dev->conf.physical_block_size != 512) + { + error_report("Physical and logical block size must be 512 for floppy"); + return -1; + } + + /* rerror/werror aren't supported by fdc and therefore not even registered + * with qdev. So set the defaults manually before they are used in + * blkconf_apply_backend_options(). */ + dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO; + dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO; + blkconf_apply_backend_options(&dev->conf); + + /* 'enospc' is the default for -drive, 'report' is what blk_new() gives us + * for empty drives. */ + if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC && + blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) { + error_report("fdc doesn't support drive option werror"); + return -1; + } + if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { + error_report("fdc doesn't support drive option rerror"); + return -1; + } + + drive->blk = dev->conf.blk; + drive->fdctrl = bus->fdc; + + fd_init(drive); + blk_set_dev_ops(drive->blk, &fd_block_ops, drive); + + /* Keep 'type' qdev property and FDrive->drive in sync */ + drive->drive = dev->type; + pick_drive_type(drive); + dev->type = drive->drive; + + fd_revalidate(drive); + + return 0; +} + +static void floppy_drive_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->init = floppy_drive_init; + set_bit(DEVICE_CATEGORY_STORAGE, k->categories); + k->bus_type = TYPE_FLOPPY_BUS; + k->props = floppy_drive_properties; + k->desc = "virtual floppy drive"; +} + +static const TypeInfo floppy_drive_info = { + .name = TYPE_FLOPPY_DRIVE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FloppyDrive), + .class_init = floppy_drive_class_init, +}; + /********************************************************/ /* Intel 82078 floppy disk controller emulation */ @@ -684,8 +841,13 @@ struct FDCtrl { /* Power down config (also with status regB access mode */ uint8_t pwrd; /* Floppy drives */ + FloppyBus bus; uint8_t num_floppies; FDrive drives[MAX_FD]; + struct { + BlockBackend *blk; + FloppyDriveType type; + } qdev_for_drives[MAX_FD]; int reset_sensei; uint32_t check_media_rate; FloppyDriveType fallback; /* type=auto failure fallback */ @@ -1159,9 +1321,9 @@ static inline FDrive *drv3(FDCtrl *fdctrl) } #endif -static FDrive *get_cur_drv(FDCtrl *fdctrl) +static FDrive *get_drv(FDCtrl *fdctrl, int unit) { - switch (fdctrl->cur_drv) { + switch (unit) { case 0: return drv0(fdctrl); case 1: return drv1(fdctrl); #if MAX_FD == 4 @@ -1172,6 +1334,11 @@ static FDrive *get_cur_drv(FDCtrl *fdctrl) } } +static FDrive *get_cur_drv(FDCtrl *fdctrl) +{ + return get_drv(fdctrl, fdctrl->cur_drv); +} + /* Status A register : 0x00 (read-only) */ static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl) { @@ -2331,46 +2498,49 @@ static void fdctrl_result_timer(void *opaque) } } -static void fdctrl_change_cb(void *opaque, bool load) -{ - FDrive *drive = opaque; - - drive->media_changed = 1; - drive->media_validated = false; - fd_revalidate(drive); -} - -static const BlockDevOps fdctrl_block_ops = { - .change_media_cb = fdctrl_change_cb, -}; - /* Init functions */ -static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) +static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp, + DeviceState *fdc_dev) { unsigned int i; FDrive *drive; + DeviceState *dev; + BlockBackend *blk; + Error *local_err = NULL; for (i = 0; i < MAX_FD; i++) { drive = &fdctrl->drives[i]; drive->fdctrl = fdctrl; - if (drive->blk) { - if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_setg(errp, "fdc doesn't support drive option werror"); - return; - } - if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "fdc doesn't support drive option rerror"); - return; - } + /* If the drive is not present, we skip creating the qdev device, but + * still have to initialise the controller. */ + blk = fdctrl->qdev_for_drives[i].blk; + if (!blk) { + fd_init(drive); + fd_revalidate(drive); + continue; + } + + dev = qdev_create(&fdctrl->bus.bus, "floppy"); + qdev_prop_set_uint32(dev, "unit", i); + qdev_prop_set_enum(dev, "drive-type", fdctrl->qdev_for_drives[i].type); + + blk_ref(blk); + blk_detach_dev(blk, fdc_dev); + fdctrl->qdev_for_drives[i].blk = NULL; + qdev_prop_set_drive(dev, "drive", blk, &local_err); + blk_unref(blk); + + if (local_err) { + error_propagate(errp, local_err); + return; } - fd_init(drive); - if (drive->blk) { - blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); - pick_drive_type(drive); + object_property_set_bool(OBJECT(dev), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } - fd_revalidate(drive); } } @@ -2442,7 +2612,8 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, *fdc_tc = qdev_get_gpio_in(dev, 0); } -static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) +static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl, + Error **errp) { int i, j; static int command_tables_inited = 0; @@ -2480,7 +2651,9 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) k->register_channel(fdctrl->dma, fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl); } - fdctrl_connect_drives(fdctrl, errp); + + floppy_bus_create(fdctrl, &fdctrl->bus, dev); + fdctrl_connect_drives(fdctrl, errp, dev); } static const MemoryRegionPortio fdc_portio_list[] = { @@ -2508,7 +2681,7 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) } qdev_set_legacy_instance_id(dev, isa->iobase, 2); - fdctrl_realize_common(fdctrl, &err); + fdctrl_realize_common(dev, fdctrl, &err); if (err != NULL) { error_propagate(errp, err); return; @@ -2559,7 +2732,7 @@ static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp) FDCtrlSysBus *sys = SYSBUS_FDC(dev); FDCtrl *fdctrl = &sys->state; - fdctrl_realize_common(fdctrl, errp); + fdctrl_realize_common(dev, fdctrl, errp); } FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) @@ -2606,14 +2779,14 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), - DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].blk), - DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk), + DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.qdev_for_drives[0].blk), + DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.qdev_for_drives[1].blk), DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, 0, true), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.drives[0].drive, + DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.drives[1].drive, + DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, @@ -2665,12 +2838,12 @@ static const VMStateDescription vmstate_sysbus_fdc ={ }; static Property sysbus_fdc_properties[] = { - DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk), - DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.drives[0].drive, + DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.qdev_for_drives[0].blk), + DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.qdev_for_drives[1].blk), + DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.drives[1].drive, + DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.qdev_for_drives[1].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, @@ -2695,8 +2868,8 @@ static const TypeInfo sysbus_fdc_info = { }; static Property sun4m_fdc_properties[] = { - DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk), - DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.drives[0].drive, + DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.qdev_for_drives[0].blk), + DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, @@ -2744,6 +2917,8 @@ static void fdc_register_types(void) type_register_static(&sysbus_fdc_type_info); type_register_static(&sysbus_fdc_info); type_register_static(&sun4m_fdc_info); + type_register_static(&floppy_bus_info); + type_register_static(&floppy_drive_info); } type_init(fdc_register_types) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index cef3bb42f1..d479fd22f5 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -258,8 +258,10 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, req->has_sg = true; dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct); req->aiocb = is_write ? - dma_blk_write(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req) : - dma_blk_read(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req); + dma_blk_write(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, + nvme_rw_cb, req) : + dma_blk_read(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, + nvme_rw_cb, req); return NVME_NO_COMPLETE; } @@ -373,7 +375,7 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) if (!cqid || nvme_check_cqid(n, cqid)) { return NVME_INVALID_CQID | NVME_DNR; } - if (!sqid || (sqid && !nvme_check_sqid(n, sqid))) { + if (!sqid || !nvme_check_sqid(n, sqid)) { return NVME_INVALID_QID | NVME_DNR; } if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { @@ -447,7 +449,7 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) uint16_t qflags = le16_to_cpu(c->cq_flags); uint64_t prp1 = le64_to_cpu(c->prp1); - if (!cqid || (cqid && !nvme_check_cqid(n, cqid))) { + if (!cqid || !nvme_check_cqid(n, cqid)) { return NVME_INVALID_CQID | NVME_DNR; } if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 1292a4b459..3a7dc194e2 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -167,12 +167,12 @@ static void destroy_grant(gpointer pgnt) xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_be_printf(&grant->blkdev->xendev, 0, + xen_pv_printf(&grant->blkdev->xendev, 0, "xengnttab_unmap failed: %s\n", strerror(errno)); } grant->blkdev->persistent_gnt_count--; - xen_be_printf(&grant->blkdev->xendev, 3, + xen_pv_printf(&grant->blkdev->xendev, 3, "unmapped grant %p\n", grant->page); g_free(grant); } @@ -184,11 +184,11 @@ static void remove_persistent_region(gpointer data, gpointer dev) xengnttab_handle *gnt = blkdev->xendev.gnttabdev; if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_be_printf(&blkdev->xendev, 0, + xen_pv_printf(&blkdev->xendev, 0, "xengnttab_unmap region %p failed: %s\n", region->addr, strerror(errno)); } - xen_be_printf(&blkdev->xendev, 3, + xen_pv_printf(&blkdev->xendev, 3, "unmapped grant region %p with %d pages\n", region->addr, region->num); g_free(region); @@ -255,7 +255,7 @@ static int ioreq_parse(struct ioreq *ioreq) size_t len; int i; - xen_be_printf(&blkdev->xendev, 3, + xen_pv_printf(&blkdev->xendev, 3, "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", ioreq->req.operation, ioreq->req.nr_segments, ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); @@ -275,28 +275,28 @@ static int ioreq_parse(struct ioreq *ioreq) case BLKIF_OP_DISCARD: return 0; default: - xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + xen_pv_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", ioreq->req.operation); goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + xen_pv_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); goto err; } ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + xen_pv_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); goto err; } if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + xen_pv_printf(&blkdev->xendev, 0, "error: first > last sector\n"); goto err; } if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + xen_pv_printf(&blkdev->xendev, 0, "error: page crossing\n"); goto err; } @@ -308,7 +308,7 @@ static int ioreq_parse(struct ioreq *ioreq) qemu_iovec_add(&ioreq->v, (void*)mem, len); } if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + xen_pv_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); goto err; } return 0; @@ -331,7 +331,7 @@ static void ioreq_unmap(struct ioreq *ioreq) return; } if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(&ioreq->blkdev->xendev, 0, "xengnttab_unmap failed: %s\n", strerror(errno)); } @@ -343,7 +343,7 @@ static void ioreq_unmap(struct ioreq *ioreq) continue; } if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(&ioreq->blkdev->xendev, 0, "xengnttab_unmap failed: %s\n", strerror(errno)); } @@ -381,7 +381,7 @@ static int ioreq_map(struct ioreq *ioreq) if (grant != NULL) { page[i] = grant->page; - xen_be_printf(&ioreq->blkdev->xendev, 3, + xen_pv_printf(&ioreq->blkdev->xendev, 3, "using persistent-grant %" PRIu32 "\n", ioreq->refs[i]); } else { @@ -410,7 +410,7 @@ static int ioreq_map(struct ioreq *ioreq) ioreq->pages = xengnttab_map_grant_refs (gnt, new_maps, domids, refs, ioreq->prot); if (ioreq->pages == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(&ioreq->blkdev->xendev, 0, "can't map %d grant refs (%s, %d maps)\n", new_maps, strerror(errno), ioreq->blkdev->cnt_map); return -1; @@ -426,7 +426,7 @@ static int ioreq_map(struct ioreq *ioreq) ioreq->page[i] = xengnttab_map_grant_ref (gnt, domids[i], refs[i], ioreq->prot); if (ioreq->page[i] == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(&ioreq->blkdev->xendev, 0, "can't map grant ref %d (%s, %d maps)\n", refs[i], strerror(errno), ioreq->blkdev->cnt_map); ioreq->mapped = 1; @@ -474,7 +474,7 @@ static int ioreq_map(struct ioreq *ioreq) grant->page = ioreq->page[new_maps]; } grant->blkdev = ioreq->blkdev; - xen_be_printf(&ioreq->blkdev->xendev, 3, + xen_pv_printf(&ioreq->blkdev->xendev, 3, "adding grant %" PRIu32 " page: %p\n", refs[new_maps], grant->page); g_tree_insert(ioreq->blkdev->persistent_gnts, @@ -557,7 +557,7 @@ static int ioreq_grant_copy(struct ioreq *ioreq) rc = xengnttab_grant_copy(gnt, count, segs); if (rc) { - xen_be_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(&ioreq->blkdev->xendev, 0, "failed to copy data %d\n", rc); ioreq->aio_errors++; return -1; @@ -565,7 +565,7 @@ static int ioreq_grant_copy(struct ioreq *ioreq) for (i = 0; i < count; i++) { if (segs[i].status != GNTST_okay) { - xen_be_printf(&ioreq->blkdev->xendev, 3, + xen_pv_printf(&ioreq->blkdev->xendev, 3, "failed to copy data %d for gref %d, domid %d\n", segs[i].status, ioreq->refs[i], ioreq->domids[i]); ioreq->aio_errors++; @@ -599,7 +599,7 @@ static void qemu_aio_complete(void *opaque, int ret) struct ioreq *ioreq = opaque; if (ret != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", + xen_pv_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); ioreq->aio_errors++; } @@ -796,7 +796,7 @@ static void blk_send_response_all(struct XenBlkDev *blkdev) ioreq_release(ioreq, true); } if (send_notify) { - xen_be_send_notify(&blkdev->xendev); + xen_pv_send_notify(&blkdev->xendev); } } @@ -866,7 +866,7 @@ static void blk_handle_requests(struct XenBlkDev *blkdev) }; if (blk_send_response_one(ioreq)) { - xen_be_send_notify(&blkdev->xendev); + xen_pv_send_notify(&blkdev->xendev); } ioreq_release(ioreq, false); continue; @@ -910,7 +910,7 @@ static void blk_alloc(struct XenDevice *xendev) } if (xengnttab_set_max_grants(xendev->gnttabdev, MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) { - xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", strerror(errno)); } } @@ -1056,11 +1056,11 @@ static int blk_connect(struct XenDevice *xendev) } /* setup via xenbus -> create new block driver instance */ - xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + xen_pv_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { - xen_be_printf(&blkdev->xendev, 0, "error: %s\n", + xen_pv_printf(&blkdev->xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); return -1; @@ -1068,10 +1068,11 @@ static int blk_connect(struct XenDevice *xendev) blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ - xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); + xen_pv_printf(&blkdev->xendev, 2, + "get configured bdrv (cmdline setup)\n"); blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); + xen_pv_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); blkdev->blk = NULL; return -1; } @@ -1084,13 +1085,13 @@ static int blk_connect(struct XenDevice *xendev) if (blkdev->file_size < 0) { BlockDriverState *bs = blk_bs(blkdev->blk); const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; - xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", + xen_pv_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), drv_name ?: "-"); blkdev->file_size = 0; } - xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + xen_pv_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," " size %" PRId64 " (%" PRId64 " MB)\n", blkdev->type, blkdev->fileproto, blkdev->filename, blkdev->file_size, blkdev->file_size >> 20); @@ -1174,10 +1175,10 @@ static int blk_connect(struct XenDevice *xendev) blkdev->feature_grant_copy = (xengnttab_grant_copy(blkdev->xendev.gnttabdev, 0, NULL) == 0); - xen_be_printf(&blkdev->xendev, 3, "grant copy operation %s\n", + xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", blkdev->feature_grant_copy ? "enabled" : "disabled"); - xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " "remote port %d, local port %d\n", blkdev->xendev.protocol, blkdev->ring_ref, blkdev->xendev.remote_port, blkdev->xendev.local_port); @@ -1193,7 +1194,7 @@ static void blk_disconnect(struct XenDevice *xendev) blk_unref(blkdev->blk); blkdev->blk = NULL; } - xen_be_unbind_evtchn(&blkdev->xendev); + xen_pv_unbind_evtchn(&blkdev->xendev); if (blkdev->sring) { xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index c2b9154305..def34cd0d2 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -450,7 +450,8 @@ static void cadence_uart_reset(DeviceState *dev) s->r[R_IMR] = 0; s->r[R_CISR] = 0; s->r[R_RTRIG] = 0x00000020; - s->r[R_BRGR] = 0x0000000F; + s->r[R_BRGR] = 0x0000028B; + s->r[R_BDIV] = 0x0000000F; s->r[R_TTRIG] = 0x00000020; uart_rx_reset(s); diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 86cdc529a3..c01f41090e 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -74,7 +74,7 @@ static void buffer_append(struct XenConsole *con) xen_mb(); intf->out_cons = cons; - xen_be_send_notify(&con->xendev); + xen_pv_send_notify(&con->xendev); if (buffer->max_capacity && buffer->size > buffer->max_capacity) { @@ -142,7 +142,7 @@ static void xencons_receive(void *opaque, const uint8_t *buf, int len) } xen_wmb(); intf->in_prod = prod; - xen_be_send_notify(&con->xendev); + xen_pv_send_notify(&con->xendev); } static void xencons_send(struct XenConsole *con) @@ -158,16 +158,17 @@ static void xencons_send(struct XenConsole *con) len = size; } if (len < 1) { - if (!con->backlog) { - con->backlog = 1; - xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); - } + if (!con->backlog) { + con->backlog = 1; + xen_pv_printf(&con->xendev, 1, + "backlog piling up, nobody listening?\n"); + } } else { - buffer_advance(&con->buffer, len); - if (con->backlog && len == size) { - con->backlog = 0; - xen_be_printf(&con->xendev, 1, "backlog is gone\n"); - } + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_pv_printf(&con->xendev, 1, "backlog is gone\n"); + } } } @@ -191,7 +192,7 @@ static int con_init(struct XenDevice *xendev) type = xenstore_read_str(con->console, "type"); if (!type || strcmp(type, "ioemu") != 0) { - xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type); ret = -1; goto out; } @@ -247,7 +248,8 @@ static int con_initialise(struct XenDevice *xendev) qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, xencons_receive, NULL, con, NULL, true); - xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + xen_pv_printf(xendev, 1, + "ring mfn %d, remote port %d, local port %d, limit %zd\n", con->ring_ref, con->xendev.remote_port, con->xendev.local_port, @@ -260,7 +262,7 @@ static void con_disconnect(struct XenDevice *xendev) struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); qemu_chr_fe_deinit(&con->chr); - xen_be_unbind_evtchn(&con->xendev); + xen_pv_unbind_evtchn(&con->xendev); if (con->sring) { if (!xendev->dev) { diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c index 9c0018448a..5c666f9b24 100644 --- a/hw/display/milkymist-tmu2.c +++ b/hw/display/milkymist-tmu2.c @@ -213,7 +213,7 @@ static void tmu2_start(MilkymistTMU2State *s) /* Read the QEMU source framebuffer into an OpenGL texture */ glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); - fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES]; + fb_len = 2ULL * s->regs[R_TEXHRES] * s->regs[R_TEXVRES]; fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0); if (fb == NULL) { glDeleteTextures(1, &texture); diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index fa6fd0e53f..60bce94d6b 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -333,6 +333,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, qemu_log_mask(LOG_GUEST_ERROR, "%s: host couldn't handle guest format %d\n", __func__, c2d.format); + g_free(res); cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 46b7d5eded..7a8727aa21 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -90,28 +90,29 @@ static int common_bind(struct common *c) xen_pfn_t mfn; if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &val) == -1) - return -1; + return -1; mfn = (xen_pfn_t)val; assert(val == mfn); if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1) - return -1; + return -1; c->page = xenforeignmemory_map(xen_fmem, c->xendev.dom, PROT_READ | PROT_WRITE, 1, &mfn, NULL); if (c->page == NULL) - return -1; + return -1; xen_be_bind_evtchn(&c->xendev); - xen_be_printf(&c->xendev, 1, "ring mfn %"PRI_xen_pfn", remote-port %d, local-port %d\n", - mfn, c->xendev.remote_port, c->xendev.local_port); + xen_pv_printf(&c->xendev, 1, + "ring mfn %"PRI_xen_pfn", remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); return 0; } static void common_unbind(struct common *c) { - xen_be_unbind_evtchn(&c->xendev); + xen_pv_unbind_evtchn(&c->xendev); if (c->page) { xenforeignmemory_unmap(xen_fmem, c->page, 1); c->page = NULL; @@ -214,7 +215,7 @@ static int xenfb_kbd_event(struct XenInput *xenfb, XENKBD_IN_RING_REF(page, prod) = *event; xen_wmb(); /* ensure ring contents visible */ page->in_prod = prod + 1; - return xen_be_send_notify(&xenfb->c.xendev); + return xen_pv_send_notify(&xenfb->c.xendev); } /* Send a keyboard (or mouse button) event */ @@ -345,7 +346,7 @@ static int input_initialise(struct XenDevice *xendev) int rc; if (!in->c.con) { - xen_be_printf(xendev, 1, "ds not set (yet)\n"); + xen_pv_printf(xendev, 1, "ds not set (yet)\n"); return -1; } @@ -396,7 +397,7 @@ static void input_event(struct XenDevice *xendev) if (page->out_prod == page->out_cons) return; page->out_cons = page->out_prod; - xen_be_send_notify(&xenfb->c.xendev); + xen_pv_send_notify(&xenfb->c.xendev); } /* -------------------------------------------------------------------- */ @@ -500,8 +501,8 @@ out: } static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, - int width, int height, int depth, - size_t fb_len, int offset, int row_stride) + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) { size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; @@ -510,40 +511,47 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, int max_width, max_height; if (fb_len_lim > fb_len_max) { - xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", - fb_len_lim, fb_len_max); - fb_len_lim = fb_len_max; + xen_pv_printf(&xenfb->c.xendev, 0, + "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; } if (fb_len_lim && fb_len > fb_len_lim) { - xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", - fb_len, fb_len_lim); - fb_len = fb_len_lim; + xen_pv_printf(&xenfb->c.xendev, 0, + "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; } if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { - xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n", - depth); - return -1; + xen_pv_printf(&xenfb->c.xendev, 0, + "can't handle frontend fb depth %d\n", + depth); + return -1; } if (row_stride <= 0 || row_stride > fb_len) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); - return -1; + xen_pv_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", + row_stride); + return -1; } max_width = row_stride / (depth / 8); if (width < 0 || width > max_width) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", - width, max_width); - width = max_width; + xen_pv_printf(&xenfb->c.xendev, 0, + "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; } if (offset < 0 || offset >= fb_len) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", - offset, fb_len - 1); - return -1; + xen_pv_printf(&xenfb->c.xendev, 0, + "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; } max_height = (fb_len - offset) / row_stride; if (height < 0 || height > max_height) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", - height, max_height); - height = max_height; + xen_pv_printf(&xenfb->c.xendev, 0, + "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; } xenfb->fb_len = fb_len; xenfb->row_stride = row_stride; @@ -553,8 +561,9 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, xenfb->offset = offset; xenfb->up_fullscreen = 1; xenfb->do_resize = 1; - xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", - width, height, depth, offset, row_stride); + xen_pv_printf(&xenfb->c.xendev, 1, + "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); return 0; } @@ -631,7 +640,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) } } if (oops) /* should not happen */ - xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", + xen_pv_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", __FUNCTION__, xenfb->depth, bpp); dpy_gfx_update(xenfb->c.con, x, y, w, h); @@ -663,7 +672,7 @@ static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) xen_wmb(); /* ensure ring contents visible */ page->in_prod = prod + 1; - xen_be_send_notify(&xenfb->c.xendev); + xen_pv_send_notify(&xenfb->c.xendev); } static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) @@ -696,9 +705,9 @@ static void xenfb_update(void *opaque) return; if (!xenfb->feature_update) { - /* we don't get update notifications, thus use the - * sledge hammer approach ... */ - xenfb->up_fullscreen = 1; + /* we don't get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; } /* resize if needed */ @@ -721,7 +730,8 @@ static void xenfb_update(void *opaque) break; } dpy_gfx_replace_surface(xenfb->c.con, surface); - xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", + xen_pv_printf(&xenfb->c.xendev, 1, + "update: resizing: %dx%d @ %d bpp%s\n", xenfb->width, xenfb->height, xenfb->depth, is_buffer_shared(surface) ? " (shared)" : ""); xenfb->up_fullscreen = 1; @@ -729,18 +739,19 @@ static void xenfb_update(void *opaque) /* run queued updates */ if (xenfb->up_fullscreen) { - xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); - xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + xen_pv_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); } else if (xenfb->up_count) { - xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); - for (i = 0; i < xenfb->up_count; i++) - xenfb_guest_copy(xenfb, - xenfb->up_rects[i].x, - xenfb->up_rects[i].y, - xenfb->up_rects[i].w, - xenfb->up_rects[i].h); + xen_pv_printf(&xenfb->c.xendev, 3, "update: %d rects\n", + xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); } else { - xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + xen_pv_printf(&xenfb->c.xendev, 3, "update: nothing\n"); } xenfb->up_count = 0; xenfb->up_fullscreen = 0; @@ -794,14 +805,14 @@ static void xenfb_handle_events(struct XenFB *xenfb) w = MIN(event->update.width, xenfb->width - x); h = MIN(event->update.height, xenfb->height - y); if (w < 0 || h < 0) { - xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n"); + xen_pv_printf(&xenfb->c.xendev, 1, "bogus update ignored\n"); break; } if (x != event->update.x || y != event->update.y || w != event->update.width || h != event->update.height) { - xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n"); + xen_pv_printf(&xenfb->c.xendev, 1, "bogus update clipped\n"); } if (w == xenfb->width && h > xenfb->height / 2) { /* scroll detector: updated more than 50% of the lines, @@ -883,7 +894,7 @@ static int fb_initialise(struct XenDevice *xendev) if (fb->feature_update) xenstore_write_be_int(xendev, "request-update", 1); - xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + xen_pv_printf(xendev, 1, "feature-update=%d, videoram=%d\n", fb->feature_update, videoram); return 0; } @@ -902,7 +913,7 @@ static void fb_disconnect(struct XenDevice *xendev) PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); if (fb->pixels == MAP_FAILED) { - xen_be_printf(xendev, 0, + xen_pv_printf(xendev, 0, "Couldn't replace the framebuffer with anonymous memory errno=%d\n", errno); } @@ -923,7 +934,7 @@ static void fb_frontend_changed(struct XenDevice *xendev, const char *node) if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 && xendev->fe_state == XenbusStateConnected && xendev->be_state == XenbusStateConnected) { - xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n"); + xen_pv_printf(xendev, 2, "re-trigger connected (frontend bug)\n"); xen_be_set_state(xendev, XenbusStateConnected); fb->bug_trigger = 1; /* only once */ } @@ -934,7 +945,7 @@ static void fb_event(struct XenDevice *xendev) struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); xenfb_handle_events(xenfb); - xen_be_send_notify(&xenfb->c.xendev); + xen_pv_send_notify(&xenfb->c.xendev); } /* -------------------------------------------------------------------- */ @@ -977,14 +988,14 @@ void xen_init_display(int domid) wait_more: i++; main_loop_wait(true); - xfb = xen_be_find_xendev("vfb", domid, 0); - xin = xen_be_find_xendev("vkbd", domid, 0); + xfb = xen_pv_find_xendev("vfb", domid, 0); + xin = xen_pv_find_xendev("vkbd", domid, 0); if (!xfb || !xin) { if (i < 256) { usleep(10000); goto wait_more; } - xen_be_printf(NULL, 1, "displaystate setup failed\n"); + xen_pv_printf(NULL, 1, "displaystate setup failed\n"); return; } diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index f3574aa8f3..c36c394fda 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -237,7 +237,7 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, break; case ISR_ADDR: - s->isr |= ~value; + s->isr &= ~value; imx_gpio_set_all_int_lines(s); break; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 93be96f89c..5cd1da9a87 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -53,7 +53,6 @@ #include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" #include "hw/i386/x86-iommu.h" -#include "hw/timer/hpet.h" #include "hw/acpi/aml-build.h" diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 63ead21047..3c19bdadc5 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1009,6 +1009,7 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) &ncq_tfs->sglist, BLOCK_ACCT_READ); ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist, ncq_tfs->lba << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: @@ -1022,6 +1023,7 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) &ncq_tfs->sglist, BLOCK_ACCT_WRITE); ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, ncq_tfs->lba << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, ncq_cb, ncq_tfs); break; default: diff --git a/hw/ide/core.c b/hw/ide/core.c index 7291677109..43709e545f 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -882,15 +882,15 @@ static void ide_dma_cb(void *opaque, int ret) switch (s->dma_cmd) { case IDE_DMA_READ: s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, - ide_dma_cb, s); + BDRV_SECTOR_SIZE, ide_dma_cb, s); break; case IDE_DMA_WRITE: s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, offset, - ide_dma_cb, s); + BDRV_SECTOR_SIZE, ide_dma_cb, s); break; case IDE_DMA_TRIM: s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), - &s->sg, offset, + &s->sg, offset, BDRV_SECTOR_SIZE, ide_issue_trim, s->blk, ide_dma_cb, s, DMA_DIRECTION_TO_DEVICE); break; diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 76f97c2539..9742c005d1 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -52,187 +52,6 @@ static const int debug_macio = 0; #define MACIO_PAGE_SIZE 4096 -/* - * Unaligned DMA read/write access functions required for OS X/Darwin which - * don't perform DMA transactions on sector boundaries. These functions are - * modelled on bdrv_co_preadv()/bdrv_co_pwritev() and so should be easy to - * remove if the unaligned block APIs are ever exposed. - */ - -static void pmac_dma_read(BlockBackend *blk, - int64_t offset, unsigned int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr; - int64_t sector_num; - int nsector; - uint64_t align = BDRV_SECTOR_SIZE; - size_t head_bytes, tail_bytes; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - sector_num = (offset >> 9); - nsector = (io->len >> 9); - - MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): " - "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, - sector_num, nsector); - - dma_addr = io->addr; - io->dir = DMA_DIRECTION_FROM_DEVICE; - io->dma_len = io->len; - io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, - io->dir); - - if (offset & (align - 1)) { - head_bytes = offset & (align - 1); - - MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", " - "discarding %zu bytes\n", sector_num, head_bytes); - - qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - - bytes += offset & (align - 1); - offset = offset & ~(align - 1); - } - - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - - if ((offset + bytes) & (align - 1)) { - tail_bytes = (offset + bytes) & (align - 1); - - MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", " - "discarding bytes %zu\n", sector_num, tail_bytes); - - qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes); - bytes = ROUND_UP(bytes, align); - } - - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - - io->len = 0; - - MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " - "nsector: %x\n", (offset >> 9), (bytes >> 9)); - - s->bus->dma->aiocb = blk_aio_preadv(blk, offset, &io->iov, 0, cb, io); -} - -static void pmac_dma_write(BlockBackend *blk, - int64_t offset, int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr; - int64_t sector_num; - int nsector; - uint64_t align = BDRV_SECTOR_SIZE; - size_t head_bytes, tail_bytes; - bool unaligned_head = false, unaligned_tail = false; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - sector_num = (offset >> 9); - nsector = (io->len >> 9); - - MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): " - "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, - sector_num, nsector); - - dma_addr = io->addr; - io->dir = DMA_DIRECTION_TO_DEVICE; - io->dma_len = io->len; - io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, - io->dir); - - if (offset & (align - 1)) { - head_bytes = offset & (align - 1); - sector_num = ((offset & ~(align - 1)) >> 9); - - MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %" - PRId64 "\n", sector_num); - - blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align); - - qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - - bytes += offset & (align - 1); - offset = offset & ~(align - 1); - - unaligned_head = true; - } - - if ((offset + bytes) & (align - 1)) { - tail_bytes = (offset + bytes) & (align - 1); - sector_num = (((offset + bytes) & ~(align - 1)) >> 9); - - MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %" - PRId64 "\n", sector_num); - - blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align); - - if (!unaligned_head) { - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - } - - qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes, - align - tail_bytes); - - bytes = ROUND_UP(bytes, align); - - unaligned_tail = true; - } - - if (!unaligned_head && !unaligned_tail) { - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - } - - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - - io->len = 0; - - MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " - "nsector: %x\n", (offset >> 9), (bytes >> 9)); - - s->bus->dma->aiocb = blk_aio_pwritev(blk, offset, &io->iov, 0, cb, io); -} - -static void pmac_dma_trim(BlockBackend *blk, - int64_t offset, int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - dma_addr = io->addr; - io->dir = DMA_DIRECTION_TO_DEVICE; - io->dma_len = io->len; - io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, - io->dir); - - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - io->len = 0; - - s->bus->dma->aiocb = ide_issue_trim(offset, &io->iov, cb, io, blk); -} - static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; @@ -244,6 +63,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (ret < 0) { MACIO_DPRINTF("DMA error: %d\n", ret); + qemu_sglist_destroy(&s->sg); ide_atapi_io_error(s, ret); goto done; } @@ -258,6 +78,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (s->io_buffer_size <= 0) { MACIO_DPRINTF("End of IDE transfer\n"); + qemu_sglist_destroy(&s->sg); ide_atapi_cmd_ok(s); m->dma_active = false; goto done; @@ -282,7 +103,15 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) /* Calculate current offset */ offset = ((int64_t)s->lba << 11) + s->io_buffer_index; - pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io); + qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, + &address_space_memory); + qemu_sglist_add(&s->sg, io->addr, io->len); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; + io->len = 0; + + s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, 0x1, + pmac_ide_atapi_transfer_cb, io); return; done: @@ -310,6 +139,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (ret < 0) { MACIO_DPRINTF("DMA error: %d\n", ret); + qemu_sglist_destroy(&s->sg); ide_dma_error(s); goto done; } @@ -324,6 +154,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (s->io_buffer_size <= 0) { MACIO_DPRINTF("End of IDE transfer\n"); + qemu_sglist_destroy(&s->sg); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); m->dma_active = false; @@ -338,15 +169,27 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) /* Calculate number of sectors */ offset = (ide_get_sector(s) << 9) + s->io_buffer_index; + qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, + &address_space_memory); + qemu_sglist_add(&s->sg, io->addr, io->len); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; + io->len = 0; + switch (s->dma_cmd) { case IDE_DMA_READ: - pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io); + s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, 0x1, + pmac_ide_atapi_transfer_cb, io); break; case IDE_DMA_WRITE: - pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io); + s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, offset, 0x1, + pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: - pmac_dma_trim(s->blk, offset, io->len, pmac_ide_transfer_cb, io); + s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), &s->sg, + offset, 0x1, ide_issue_trim, s->blk, + pmac_ide_transfer_cb, io, + DMA_DIRECTION_TO_DEVICE); break; default: abort(); diff --git a/hw/input/adb.c b/hw/input/adb.c index 3d39368909..43d3205472 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -396,9 +396,15 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, d->devaddr = buf[1] & 0xf; break; default: - /* XXX: check this */ d->devaddr = buf[1] & 0xf; - d->handler = buf[2]; + /* we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; + } break; } } @@ -437,6 +443,7 @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) { return; } + /* FIXME: take handler into account when translating qcode */ keycode = qcode_to_adb_keycode[qcode]; if (keycode == NO_KEY) { /* We don't want to send this to the guest */ ADB_DPRINTF("Ignoring NO_KEY\n"); @@ -631,8 +638,21 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, d->devaddr = buf[1] & 0xf; break; default: - /* XXX: check this */ d->devaddr = buf[1] & 0xf; + /* we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; + } break; } } diff --git a/hw/intc/xics.c b/hw/intc/xics.c index f40b00003a..095c16a300 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -35,6 +35,8 @@ #include "hw/ppc/xics.h" #include "qemu/error-report.h" #include "qapi/visitor.h" +#include "monitor/monitor.h" +#include "hw/intc/intc.h" int xics_get_cpu_index_by_dt_id(int cpu_dt_id) { @@ -90,6 +92,47 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu) } } +static void xics_common_pic_print_info(InterruptStatsProvider *obj, + Monitor *mon) +{ + XICSState *xics = XICS_COMMON(obj); + ICSState *ics; + uint32_t i; + + for (i = 0; i < xics->nr_servers; i++) { + ICPState *icp = &xics->ss[i]; + + if (!icp->output) { + continue; + } + monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", + i, icp->xirr, icp->xirr_owner, + icp->pending_priority, icp->mfrr); + } + + QLIST_FOREACH(ics, &xics->ics, list) { + monitor_printf(mon, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); + + if (!ics->irqs) { + continue; + } + + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = ics->irqs + i; + + if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { + continue; + } + monitor_printf(mon, " %4x %s %02x %02x\n", + ics->offset + i, + (irq->flags & XICS_FLAGS_IRQ_LSI) ? + "LSI" : "MSI", + irq->priority, irq->status); + } + } +} + /* * XICS Common class - parent for emulated XICS and KVM-XICS */ @@ -140,6 +183,25 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name, info->set_nr_irqs(xics, value, errp); } +void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, + const char *typename, Error **errp) +{ + int i; + + xics->nr_servers = nr_servers; + + xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState)); + for (i = 0; i < xics->nr_servers; i++) { + char name[32]; + ICPState *icp = &xics->ss[i]; + + object_initialize(icp, sizeof(*icp), typename); + snprintf(name, sizeof(name), "icp[%d]", i); + object_property_add_child(OBJECT(xics), name, OBJECT(icp), errp); + icp->xics = xics; + } +} + static void xics_prop_get_nr_servers(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -155,7 +217,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v, Error **errp) { XICSState *xics = XICS_COMMON(obj); - XICSStateClass *info = XICS_COMMON_GET_CLASS(xics); + XICSStateClass *xsc = XICS_COMMON_GET_CLASS(xics); Error *error = NULL; int64_t value; @@ -170,8 +232,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v, return; } - assert(info->set_nr_servers); - info->set_nr_servers(xics, value, errp); + assert(xsc->set_nr_servers); + xsc->set_nr_servers(xics, value, errp); } static void xics_common_initfn(Object *obj) @@ -190,8 +252,10 @@ static void xics_common_initfn(Object *obj) static void xics_common_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); dc->reset = xics_common_reset; + ic->print_info = xics_common_pic_print_info; } static const TypeInfo xics_common_info = { @@ -201,6 +265,10 @@ static const TypeInfo xics_common_info = { .class_size = sizeof(XICSStateClass), .instance_init = xics_common_initfn, .class_init = xics_common_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, }; /* @@ -258,22 +326,20 @@ static void icp_check_ipi(ICPState *ss) qemu_irq_raise(ss->output); } -static void icp_resend(XICSState *xics, int server) +static void icp_resend(ICPState *ss) { - ICPState *ss = xics->ss + server; ICSState *ics; if (ss->mfrr < CPPR(ss)) { icp_check_ipi(ss); } - QLIST_FOREACH(ics, &xics->ics, list) { + QLIST_FOREACH(ics, &ss->xics->ics, list) { ics_resend(ics); } } -void icp_set_cppr(XICSState *xics, int server, uint8_t cppr) +void icp_set_cppr(ICPState *ss, uint8_t cppr) { - ICPState *ss = xics->ss + server; uint8_t old_cppr; uint32_t old_xisr; @@ -293,15 +359,13 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr) } } else { if (!XISR(ss)) { - icp_resend(xics, server); + icp_resend(ss); } } } -void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr) +void icp_set_mfrr(ICPState *ss, uint8_t mfrr) { - ICPState *ss = xics->ss + server; - ss->mfrr = mfrr; if (mfrr < CPPR(ss)) { icp_check_ipi(ss); @@ -330,23 +394,22 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr) return ss->xirr; } -void icp_eoi(XICSState *xics, int server, uint32_t xirr) +void icp_eoi(ICPState *ss, uint32_t xirr) { - ICPState *ss = xics->ss + server; ICSState *ics; uint32_t irq; /* Send EOI -> ICS */ ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); - trace_xics_icp_eoi(server, xirr, ss->xirr); + trace_xics_icp_eoi(ss->cs->cpu_index, xirr, ss->xirr); irq = xirr & XISR_MASK; - QLIST_FOREACH(ics, &xics->ics, list) { + QLIST_FOREACH(ics, &ss->xics->ics, list) { if (ics_valid_irq(ics, irq)) { ics_eoi(ics, irq); } } if (!XISR(ss)) { - icp_resend(xics, server); + icp_resend(ss); } } @@ -605,7 +668,7 @@ static int ics_simple_post_load(ICSState *ics, int version_id) int i; for (i = 0; i < ics->xics->nr_servers; i++) { - icp_resend(ics->xics, i); + icp_resend(&ics->xics->ss[i]); } return 0; diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 9c2f198fd1..17694eaa87 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -373,18 +373,7 @@ static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp) { - int i; - - xics->nr_servers = nr_servers; - - xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState)); - for (i = 0; i < xics->nr_servers; i++) { - char buffer[32]; - object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_KVM_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]), - errp); - } + xics_set_nr_servers(xics, nr_servers, TYPE_KVM_ICP, errp); } static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index e8d0623c2c..2e3f1c5e95 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -32,6 +32,7 @@ #include "qemu/timer.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" +#include "hw/ppc/fdt.h" #include "qapi/visitor.h" #include "qapi/error.h" @@ -43,9 +44,10 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; target_ulong cppr = args[0]; - icp_set_cppr(spapr->xics, cs->cpu_index, cppr); + icp_set_cppr(icp, cppr); return H_SUCCESS; } @@ -59,7 +61,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } - icp_set_mfrr(spapr->xics, server, mfrr); + icp_set_mfrr(spapr->xics->ss + server, mfrr); return H_SUCCESS; } @@ -67,7 +69,8 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); - uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; + uint32_t xirr = icp_accept(icp); args[0] = xirr; return H_SUCCESS; @@ -77,8 +80,8 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); - ICPState *ss = &spapr->xics->ss[cs->cpu_index]; - uint32_t xirr = icp_accept(ss); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; + uint32_t xirr = icp_accept(icp); args[0] = xirr; args[1] = cpu_get_host_ticks(); @@ -89,9 +92,10 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; target_ulong xirr = args[0]; - icp_eoi(spapr->xics, cs->cpu_index, xirr); + icp_eoi(icp, xirr); return H_SUCCESS; } @@ -99,8 +103,9 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; uint32_t mfrr; - uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr); + uint32_t xirr = icp_ipoll(icp, &mfrr); args[0] = xirr; args[1] = mfrr; @@ -249,18 +254,7 @@ static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp) { - int i; - - xics->nr_servers = nr_servers; - - xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState)); - for (i = 0; i < xics->nr_servers; i++) { - char buffer[32]; - object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]), - errp); - } + xics_set_nr_servers(xics, nr_servers, TYPE_ICP, errp); } static void xics_spapr_realize(DeviceState *dev, Error **errp) @@ -456,6 +450,27 @@ void xics_spapr_free(XICSState *xics, int irq, int num) } } +void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle) +{ + uint32_t interrupt_server_ranges_prop[] = { + 0, cpu_to_be32(xics->nr_servers), + }; + int node; + + _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); + + _FDT(fdt_setprop_string(fdt, node, "device_type", + "PowerPC-External-Interrupt-Presentation")); + _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp")); + _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); + _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges", + interrupt_server_ranges_prop, + sizeof(interrupt_server_ranges_prop))); + _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); + _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle)); + _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle)); +} + static void xics_spapr_register_types(void) { type_register_static(&xics_spapr_info); diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 9eebb1a521..1834d22a61 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -30,7 +30,6 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/error-report.h" -#include "qemu-common.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "hw/loader.h" diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index cf9bd3eb45..cf48f420cc 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -47,7 +47,6 @@ #include "elf.h" #include "hw/timer/mc146818rtc.h" #include "hw/timer/i8254.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" /* SysBusDevice */ diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c index 1da21a643e..3ca25894f1 100644 --- a/hw/misc/milkymist-pfpu.c +++ b/hw/misc/milkymist-pfpu.c @@ -137,7 +137,7 @@ struct MilkymistPFPUState { }; typedef struct MilkymistPFPUState MilkymistPFPUState; -static inline hwaddr +static inline uint32_t get_dma_address(uint32_t base, uint32_t x, uint32_t y) { return base + 8 * (128 * y + x); diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 6505983c12..2b11499829 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -1278,11 +1278,10 @@ e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc; - memset(d, 0, sizeof(*d)); - assert(!rss_info->enabled); d->length = cpu_to_le16(length); + d->csum = 0; e1000e_build_rx_metadata(core, pkt, pkt != NULL, rss_info, @@ -1291,6 +1290,7 @@ e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, &d->special); d->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24); d->status = (uint8_t) le32_to_cpu(status_flags); + d->special = 0; } static inline void @@ -1301,7 +1301,7 @@ e1000e_write_ext_rx_descr(E1000ECore *core, uint8_t *desc, { union e1000_rx_desc_extended *d = (union e1000_rx_desc_extended *) desc; - memset(d, 0, sizeof(*d)); + memset(&d->wb, 0, sizeof(d->wb)); d->wb.upper.length = cpu_to_le16(length); @@ -1325,7 +1325,7 @@ e1000e_write_ps_rx_descr(E1000ECore *core, uint8_t *desc, union e1000_rx_desc_packet_split *d = (union e1000_rx_desc_packet_split *) desc; - memset(d, 0, sizeof(*d)); + memset(&d->wb, 0, sizeof(d->wb)); d->wb.middle.length0 = cpu_to_le16((*written)[0]); diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index bab4dbfc98..4bf71f2d85 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1843,6 +1843,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev) EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); vmstate_unregister(&pci_dev->qdev, s->vmstate, s); + g_free(s->vmstate); eeprom93xx_free(&pci_dev->qdev, s->eeprom); qemu_del_nic(s->nic); } diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index 198a01f92d..654455355f 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -302,7 +302,7 @@ static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd, uint32_t tbadr; int16_t length; int16_t status; - } xda; + } xda; s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0); tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff; tmd->length = le16_to_cpu(xda.length); @@ -664,7 +664,9 @@ static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size) static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx) { - while (idx < 1) idx += CSR_RCVRL(s); + while (idx < 1) { + idx += CSR_RCVRL(s); + } return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8)); } @@ -672,8 +674,10 @@ static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_ti { int64_t next_time = current_time + (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30; - if (next_time <= current_time) + + if (next_time <= current_time) { next_time = current_time + 1; + } return next_time; } @@ -795,13 +799,13 @@ static void pcnet_init(PCNetState *s) mode = le16_to_cpu(initblk.mode); rlen = initblk.rlen >> 4; tlen = initblk.tlen >> 4; - ladrf[0] = le16_to_cpu(initblk.ladrf[0]); - ladrf[1] = le16_to_cpu(initblk.ladrf[1]); - ladrf[2] = le16_to_cpu(initblk.ladrf[2]); - ladrf[3] = le16_to_cpu(initblk.ladrf[3]); - padr[0] = le16_to_cpu(initblk.padr[0]); - padr[1] = le16_to_cpu(initblk.padr[1]); - padr[2] = le16_to_cpu(initblk.padr[2]); + ladrf[0] = le16_to_cpu(initblk.ladrf[0]); + ladrf[1] = le16_to_cpu(initblk.ladrf[1]); + ladrf[2] = le16_to_cpu(initblk.ladrf[2]); + ladrf[3] = le16_to_cpu(initblk.ladrf[3]); + padr[0] = le16_to_cpu(initblk.padr[0]); + padr[1] = le16_to_cpu(initblk.padr[1]); + padr[2] = le16_to_cpu(initblk.padr[2]); rdra = le32_to_cpu(initblk.rdra); tdra = le32_to_cpu(initblk.tdra); } else { @@ -809,13 +813,13 @@ static void pcnet_init(PCNetState *s) s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)), (uint8_t *)&initblk, sizeof(initblk), 0); mode = le16_to_cpu(initblk.mode); - ladrf[0] = le16_to_cpu(initblk.ladrf[0]); - ladrf[1] = le16_to_cpu(initblk.ladrf[1]); - ladrf[2] = le16_to_cpu(initblk.ladrf[2]); - ladrf[3] = le16_to_cpu(initblk.ladrf[3]); - padr[0] = le16_to_cpu(initblk.padr[0]); - padr[1] = le16_to_cpu(initblk.padr[1]); - padr[2] = le16_to_cpu(initblk.padr[2]); + ladrf[0] = le16_to_cpu(initblk.ladrf[0]); + ladrf[1] = le16_to_cpu(initblk.ladrf[1]); + ladrf[2] = le16_to_cpu(initblk.ladrf[2]); + ladrf[3] = le16_to_cpu(initblk.ladrf[3]); + padr[0] = le16_to_cpu(initblk.padr[0]); + padr[1] = le16_to_cpu(initblk.padr[1]); + padr[2] = le16_to_cpu(initblk.padr[2]); rdra = le32_to_cpu(initblk.rdra); tdra = le32_to_cpu(initblk.tdra); rlen = rdra >> 29; @@ -858,12 +862,12 @@ static void pcnet_start(PCNetState *s) printf("pcnet_start\n"); #endif - if (!CSR_DTX(s)) + if (!CSR_DTX(s)) { s->csr[0] |= 0x0010; /* set TXON */ - - if (!CSR_DRX(s)) + } + if (!CSR_DRX(s)) { s->csr[0] |= 0x0020; /* set RXON */ - + } s->csr[0] &= ~0x0004; /* clear STOP bit */ s->csr[0] |= 0x0002; pcnet_poll_timer(s); @@ -925,8 +929,7 @@ static void pcnet_rdte_poll(PCNetState *s) crda); } } else { - printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n", - crda); + printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n", crda); #endif } } @@ -1168,10 +1171,11 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) #endif while (pktcount--) { - if (CSR_RCVRC(s) <= 1) + if (CSR_RCVRC(s) <= 1) { CSR_RCVRC(s) = CSR_RCVRL(s); - else + } else { CSR_RCVRC(s)--; + } } pcnet_rdte_poll(s); @@ -1207,7 +1211,7 @@ static void pcnet_transmit(PCNetState *s) s->tx_busy = 1; - txagain: +txagain: if (pcnet_tdte_poll(s)) { struct pcnet_TMD tmd; @@ -1251,7 +1255,7 @@ static void pcnet_transmit(PCNetState *s) s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); s->xmit_pos += bcnt; - + if (!GET_FIELD(tmd.status, TMDS, ENP)) { goto txdone; } @@ -1276,21 +1280,22 @@ static void pcnet_transmit(PCNetState *s) s->csr[4] |= 0x0004; /* set TXSTRT */ s->xmit_pos = -1; - txdone: +txdone: SET_FIELD(&tmd.status, TMDS, OWN, 0); TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); - if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) + if (!CSR_TOKINTD(s) + || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) { s->csr[0] |= 0x0200; /* set TINT */ - - if (CSR_XMTRC(s)<=1) + } + if (CSR_XMTRC(s) <= 1) { CSR_XMTRC(s) = CSR_XMTRL(s); - else + } else { CSR_XMTRC(s)--; - if (count--) + } + if (count--) { goto txagain; - - } else - if (s->xmit_pos >= 0) { + } + } else if (s->xmit_pos >= 0) { struct pcnet_TMD tmd; TMDLOAD(&tmd, xmit_cxda); SET_FIELD(&tmd.misc, TMDM, BUFF, 1); @@ -1301,9 +1306,9 @@ static void pcnet_transmit(PCNetState *s) s->csr[0] |= 0x0200; /* set TINT */ if (!CSR_DXSUFLO(s)) { s->csr[0] &= ~0x0010; - } else - if (count--) - goto txagain; + } else if (count--) { + goto txagain; + } } s->tx_busy = 0; @@ -1315,13 +1320,11 @@ static void pcnet_poll(PCNetState *s) pcnet_rdte_poll(s); } - if (CSR_TDMD(s) || - (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) - { + if (CSR_TDMD(s) || (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) { /* prevent recursion */ - if (s->tx_busy) + if (s->tx_busy) { return; - + } pcnet_transmit(s); } } @@ -1340,15 +1343,16 @@ static void pcnet_poll_timer(void *opaque) if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) { uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) * 33; - if (!s->timer || !now) + if (!s->timer || !now) { s->timer = now; - else { + } else { uint64_t t = now - s->timer + CSR_POLL(s); if (t > 0xffffLL) { pcnet_poll(s); CSR_POLL(s) = CSR_PINT(s); - } else + } else { CSR_POLL(s) = t; + } } timer_mod(s->poll_timer, pcnet_get_next_poll_time(s,qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))); @@ -1371,21 +1375,21 @@ static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value) val = (val & 0x007f) | (s->csr[0] & 0x7f00); /* IFF STOP, STRT and INIT are set, clear STRT and INIT */ - if ((val&7) == 7) - val &= ~3; - - if (!CSR_STOP(s) && (val & 4)) + if ((val & 7) == 7) { + val &= ~3; + } + if (!CSR_STOP(s) && (val & 4)) { pcnet_stop(s); - - if (!CSR_INIT(s) && (val & 1)) + } + if (!CSR_INIT(s) && (val & 1)) { pcnet_init(s); - - if (!CSR_STRT(s) && (val & 2)) + } + if (!CSR_STRT(s) && (val & 2)) { pcnet_start(s); - - if (CSR_TDMD(s)) + } + if (CSR_TDMD(s)) { pcnet_transmit(s); - + } return; case 1: case 2: @@ -1429,12 +1433,16 @@ static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value) case 47: /* POLLINT */ case 72: case 74: + break; case 76: /* RCVRL */ case 78: /* XMTRL */ + val = (val > 0) ? val : 512; + break; case 112: - if (CSR_STOP(s) || CSR_SPND(s)) - break; - return; + if (CSR_STOP(s) || CSR_SPND(s)) { + break; + } + return; case 3: break; case 4: @@ -1651,8 +1659,7 @@ void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val) pcnet_bcr_writew(s, s->rap, val & 0xffff); break; } - } else - if ((addr & 0x0f) == 0) { + } else if ((addr & 0x0f) == 0) { /* switch device to dword i/o mode */ pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080); #ifdef PCNET_DEBUG_IO diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 30f2ce417b..e9d215aa4d 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -860,7 +860,7 @@ static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val) rocker_msix_irq(r, val); break; case ROCKER_TEST_DMA_SIZE: - r->test_dma_size = val; + r->test_dma_size = val & 0xFFFF; break; case ROCKER_TEST_DMA_ADDR + 4: r->test_dma_addr = ((uint64_t)val) << 32 | r->lower32; diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 3345bc6b5e..f05e59c85f 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -2350,7 +2350,7 @@ static void rtl8139_cplus_transmit(RTL8139State *s) { int txcount = 0; - while (rtl8139_cplus_transmit_one(s)) + while (txcount < 64 && rtl8139_cplus_transmit_one(s)) { ++txcount; } diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 90f6943668..92f6af9620 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -531,6 +531,7 @@ static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx) VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring); + memset(&txcq_descr, 0, sizeof(txcq_descr)); txcq_descr.txdIdx = tx_ridx; txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring); diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 6856b52999..20c43a61b3 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -69,7 +69,7 @@ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, i netdev->tx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); if (notify) { - xen_be_send_notify(&netdev->xendev); + xen_pv_send_notify(&netdev->xendev); } if (i == netdev->tx_ring.req_cons) { @@ -128,30 +128,32 @@ static void net_tx_packets(struct XenNetDev *netdev) /* should not happen in theory, we don't announce the * * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ if (txreq.flags & NETTXF_extra_info) { - xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + xen_pv_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); net_tx_error(netdev, &txreq, rc); continue; } if (txreq.flags & NETTXF_more_data) { - xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + xen_pv_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); net_tx_error(netdev, &txreq, rc); continue; } #endif if (txreq.size < 14) { - xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + xen_pv_printf(&netdev->xendev, 0, "bad packet size: %d\n", + txreq.size); net_tx_error(netdev, &txreq, rc); continue; } if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { - xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + xen_pv_printf(&netdev->xendev, 0, "error: page crossing\n"); net_tx_error(netdev, &txreq, rc); continue; } - xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + xen_pv_printf(&netdev->xendev, 3, + "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", txreq.gref, txreq.offset, txreq.size, txreq.flags, (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", @@ -162,8 +164,9 @@ static void net_tx_packets(struct XenNetDev *netdev) netdev->xendev.dom, txreq.gref, PROT_READ); if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", - txreq.gref); + xen_pv_printf(&netdev->xendev, 0, + "error: tx gref dereference failed (%d)\n", + txreq.gref); net_tx_error(netdev, &txreq, rc); continue; } @@ -211,13 +214,14 @@ static void net_rx_response(struct XenNetDev *netdev, resp->status = (int16_t)st; } - xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + xen_pv_printf(&netdev->xendev, 3, + "rx response: idx %d, status %d, flags 0x%x\n", i, resp->status, resp->flags); netdev->rx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); if (notify) { - xen_be_send_notify(&netdev->xendev); + xen_pv_send_notify(&netdev->xendev); } } @@ -242,7 +246,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return 0; } if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { - xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", + xen_pv_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); return -1; } @@ -254,7 +258,8 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size netdev->xendev.dom, rxreq.gref, PROT_WRITE); if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", + xen_pv_printf(&netdev->xendev, 0, + "error: rx gref dereference failed (%d)\n", rxreq.gref); net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); return -1; @@ -328,7 +333,8 @@ static int net_connect(struct XenDevice *xendev) rx_copy = 0; } if (rx_copy == 0) { - xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n"); + xen_pv_printf(&netdev->xendev, 0, + "frontend doesn't support rx-copy.\n"); return -1; } @@ -353,7 +359,7 @@ static int net_connect(struct XenDevice *xendev) xen_be_bind_evtchn(&netdev->xendev); - xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + xen_pv_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " "remote port %d, local port %d\n", netdev->tx_ring_ref, netdev->rx_ring_ref, netdev->xendev.remote_port, netdev->xendev.local_port); @@ -366,7 +372,7 @@ static void net_disconnect(struct XenDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - xen_be_unbind_evtchn(&netdev->xendev); + xen_pv_unbind_evtchn(&netdev->xendev); if (netdev->txs) { xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index e9a66940e0..c018f6b2ff 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -1,5 +1,6 @@ common-obj-$(CONFIG_DS1225Y) += ds1225y.o common-obj-y += eeprom93xx.o common-obj-y += fw_cfg.o +common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o obj-$(CONFIG_PSERIES) += spapr_nvram.o diff --git a/hw/nvram/chrp_nvram.c b/hw/nvram/chrp_nvram.c new file mode 100644 index 0000000000..3837510dd2 --- /dev/null +++ b/hw/nvram/chrp_nvram.c @@ -0,0 +1,85 @@ +/* + * Common Hardware Reference Platform NVRAM helper functions. + * + * The CHRP NVRAM layout is used by OpenBIOS and SLOF. See CHRP + * specification, chapter 8, or the LoPAPR specification for details + * about the NVRAM layout. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "hw/hw.h" +#include "hw/nvram/chrp_nvram.h" +#include "sysemu/sysemu.h" + +static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str) +{ + int len; + + len = strlen(str) + 1; + memcpy(&nvram[addr], str, len); + + return addr + len; +} + +/** + * Create a "system partition", used for the Open Firmware + * environment variables. + */ +int chrp_nvram_create_system_partition(uint8_t *data, int min_len) +{ + ChrpNvramPartHdr *part_header; + unsigned int i; + int end; + + part_header = (ChrpNvramPartHdr *)data; + part_header->signature = CHRP_NVPART_SYSTEM; + pstrcpy(part_header->name, sizeof(part_header->name), "system"); + + end = sizeof(ChrpNvramPartHdr); + for (i = 0; i < nb_prom_envs; i++) { + end = chrp_nvram_set_var(data, end, prom_envs[i]); + } + + /* End marker */ + data[end++] = '\0'; + + end = (end + 15) & ~15; + /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for + new variables. */ + if (end < min_len) { + end = min_len; + } + chrp_nvram_finish_partition(part_header, end); + + return end; +} + +/** + * Create a "free space" partition + */ +int chrp_nvram_create_free_partition(uint8_t *data, int len) +{ + ChrpNvramPartHdr *part_header; + + part_header = (ChrpNvramPartHdr *)data; + part_header->signature = CHRP_NVPART_FREE; + pstrcpy(part_header->name, sizeof(part_header->name), "free"); + + chrp_nvram_finish_partition(part_header, len); + + return len; +} diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 92aa563929..1f0c3e9910 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -29,7 +29,6 @@ #include "hw/isa/isa.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" -#include "hw/boards.h" #include "trace.h" #include "qemu/error-report.h" #include "qemu/config-file.h" diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 24f61212ba..63f9ed1d82 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -24,8 +24,7 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" -#include "hw/nvram/openbios_firmware_abi.h" -#include "sysemu/sysemu.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/ppc/mac.h" #include "qemu/cutils.h" #include <zlib.h> @@ -146,38 +145,14 @@ static void macio_nvram_register_types(void) static void pmac_format_nvram_partition_of(MacIONVRAMState *nvr, int off, int len) { - unsigned int i; - uint32_t start = off, end; - struct OpenBIOS_nvpart_v1 *part_header; - - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]); - - // End marker - nvr->data[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for - new variables. */ - if (end < DEF_SYSTEM_SIZE) - end = DEF_SYSTEM_SIZE; - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = len; - OpenBIOS_finish_partition(part_header, end - start); + int sysp_end; + + /* OpenBIOS nvram variables partition */ + sysp_end = chrp_nvram_create_system_partition(&nvr->data[off], + DEF_SYSTEM_SIZE) + off; + + /* Free space partition */ + chrp_nvram_create_free_partition(&nvr->data[sysp_end], len - sysp_end); } #define OSX_NVRAM_SIGNATURE (0x5A) @@ -187,15 +162,15 @@ static void pmac_format_nvram_partition_osx(MacIONVRAMState *nvr, int off, int len) { uint32_t start = off; - struct OpenBIOS_nvpart_v1 *part_header; + ChrpNvramPartHdr *part_header; unsigned char *data = &nvr->data[start]; /* empty partition */ - part_header = (struct OpenBIOS_nvpart_v1 *)data; + part_header = (ChrpNvramPartHdr *)data; part_header->signature = OSX_NVRAM_SIGNATURE; pstrcpy(part_header->name, sizeof(part_header->name), "wwwwwwwwwwww"); - OpenBIOS_finish_partition(part_header, len); + chrp_nvram_finish_partition(part_header, len); /* Generation */ stl_be_p(&data[20], 2); diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 4de5f705d8..eb42ea323f 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -31,6 +31,7 @@ #include "sysemu/block-backend.h" #include "sysemu/device_tree.h" #include "hw/sysbus.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -162,6 +163,11 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) error_setg(errp, "can't read spapr-nvram contents"); return; } + } else if (nb_prom_envs > 0) { + /* Create a system partition to pass the -prom-env variables */ + chrp_nvram_create_system_partition(nvram->buf, MIN_NVRAM_SIZE / 4); + chrp_nvram_create_free_partition(&nvram->buf[MIN_NVRAM_SIZE / 4], + nvram->size - MIN_NVRAM_SIZE / 4); } spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 1cc598f7e9..6ac187fa32 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -15,7 +15,6 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" -#include "hw/pci/pci_bus.h" #include "hw/pci/pci_bridge.h" #include "hw/i386/pc.h" #include "qemu/range.h" diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 99a0d4e581..8025129377 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -4,7 +4,9 @@ obj-y += ppc.o ppc_booke.o fdt.o obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o -obj-$(CONFIG_PSERIES) += spapr_cpu_core.o +obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o +# IBM PowerNV +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c new file mode 100644 index 0000000000..82276e0857 --- /dev/null +++ b/hw/ppc/pnv.c @@ -0,0 +1,819 @@ +/* + * QEMU PowerPC PowerNV machine model + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "sysemu/numa.h" +#include "hw/hw.h" +#include "target-ppc/cpu.h" +#include "qemu/log.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_core.h" +#include "hw/loader.h" +#include "exec/address-spaces.h" +#include "qemu/cutils.h" +#include "qapi/visitor.h" + +#include "hw/ppc/pnv_xscom.h" + +#include "hw/isa/isa.h" +#include "hw/char/serial.h" +#include "hw/timer/mc146818rtc.h" + +#include <libfdt.h> + +#define FDT_MAX_SIZE 0x00100000 + +#define FW_FILE_NAME "skiboot.lid" +#define FW_LOAD_ADDR 0x0 +#define FW_MAX_SIZE 0x00400000 + +#define KERNEL_LOAD_ADDR 0x20000000 +#define INITRD_LOAD_ADDR 0x40000000 + +/* + * On Power Systems E880 (POWER8), the max cpus (threads) should be : + * 4 * 4 sockets * 12 cores * 8 threads = 1536 + * Let's make it 2^11 + */ +#define MAX_CPUS 2048 + +/* + * Memory nodes are created by hostboot, one for each range of memory + * that has a different "affinity". In practice, it means one range + * per chip. + */ +static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start, + hwaddr size) +{ + char *mem_name; + uint64_t mem_reg_property[2]; + int off; + + mem_reg_property[0] = cpu_to_be64(start); + mem_reg_property[1] = cpu_to_be64(size); + + mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start); + off = fdt_add_subnode(fdt, 0, mem_name); + g_free(mem_name); + + _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_cell(fdt, off, "ibm,chip-id", chip_id))); +} + +static int get_cpus_node(void *fdt) +{ + int cpus_offset = fdt_path_offset(fdt, "/cpus"); + + if (cpus_offset < 0) { + cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), + "cpus"); + if (cpus_offset) { + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); + } + } + _FDT(cpus_offset); + return cpus_offset; +} + +/* + * The PowerNV cores (and threads) need to use real HW ids and not an + * incremental index like it has been done on other platforms. This HW + * id is stored in the CPU PIR, it is used to create cpu nodes in the + * device tree, used in XSCOM to address cores and in interrupt + * servers. + */ +static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) +{ + CPUState *cs = CPU(DEVICE(pc->threads)); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + PowerPCCPU *cpu = POWERPC_CPU(cs); + int smt_threads = ppc_get_compat_smt_threads(cpu); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + uint32_t servers_prop[smt_threads]; + int i; + uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), + 0xffffffff, 0xffffffff}; + uint32_t tbfreq = PNV_TIMEBASE_FREQ; + uint32_t cpufreq = 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; + const uint8_t pa_features[] = { 24, 0, + 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + int offset; + char *nodename; + int cpus_offset = get_cpus_node(fdt); + + nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir); + offset = fdt_add_subnode(fdt, cpus_offset, nodename); + _FDT(offset); + g_free(nodename); + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id))); + + _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); + + _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", + env->icache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", + env->icache_line_size))); + + if (pcc->l1_dcache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", + pcc->l1_dcache_size))); + } else { + error_report("Warning: Unknown L1 dcache size for cpu"); + } + if (pcc->l1_icache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", + pcc->l1_icache_size))); + } else { + error_report("Warning: Unknown L1 icache size for cpu"); + } + + _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); + _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); + _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); + + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); + } + + if (env->mmu_model & POWERPC_MMU_1TSEG) { + _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", + segs, sizeof(segs)))); + } + + /* Advertise VMX/VSX (vector extensions) if available + * 0 / no property == no vector extensions + * 1 == VMX / Altivec available + * 2 == VSX available */ + if (env->insns_flags & PPC_ALTIVEC) { + uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1; + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx))); + } + + /* Advertise DFP (Decimal Floating Point) if available + * 0 / no property == no DFP + * 1 == DFP available */ + if (env->insns_flags2 & PPC2_DFP) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); + } + + page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop, + sizeof(page_sizes_prop)); + if (page_sizes_prop_size) { + _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features, sizeof(pa_features)))); + + if (cpu->cpu_version) { + _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version))); + } + + /* Build interrupt servers properties */ + for (i = 0; i < smt_threads; i++) { + servers_prop[i] = cpu_to_be32(pc->pir + i); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(servers_prop)))); +} + +static void powernv_populate_chip(PnvChip *chip, void *fdt) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + char *typename = pnv_core_typename(pcc->cpu_model); + size_t typesize = object_type_get_instance_size(typename); + int i; + + pnv_xscom_populate(chip, fdt, 0); + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + + powernv_create_core_node(chip, pnv_core, fdt); + } + + if (chip->ram_size) { + powernv_populate_memory_node(fdt, chip->chip_id, chip->ram_start, + chip->ram_size); + } + g_free(typename); +} + +static void *powernv_create_fdt(MachineState *machine) +{ + const char plat_compat[] = "qemu,powernv\0ibm,powernv"; + PnvMachineState *pnv = POWERNV_MACHINE(machine); + void *fdt; + char *buf; + int off; + int i; + + fdt = g_malloc0(FDT_MAX_SIZE); + _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + + /* Root node */ + _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2))); + _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2))); + _FDT((fdt_setprop_string(fdt, 0, "model", + "IBM PowerNV (emulated by qemu)"))); + _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat, + sizeof(plat_compat)))); + + buf = qemu_uuid_unparse_strdup(&qemu_uuid); + _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf))); + if (qemu_uuid_set) { + _FDT((fdt_property_string(fdt, "system-id", buf))); + } + g_free(buf); + + off = fdt_add_subnode(fdt, 0, "chosen"); + if (machine->kernel_cmdline) { + _FDT((fdt_setprop_string(fdt, off, "bootargs", + machine->kernel_cmdline))); + } + + if (pnv->initrd_size) { + uint32_t start_prop = cpu_to_be32(pnv->initrd_base); + uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size); + + _FDT((fdt_setprop(fdt, off, "linux,initrd-start", + &start_prop, sizeof(start_prop)))); + _FDT((fdt_setprop(fdt, off, "linux,initrd-end", + &end_prop, sizeof(end_prop)))); + } + + /* Populate device tree for each chip */ + for (i = 0; i < pnv->num_chips; i++) { + powernv_populate_chip(pnv->chips[i], fdt); + } + return fdt; +} + +static void ppc_powernv_reset(void) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + void *fdt; + + qemu_devices_reset(); + + fdt = powernv_create_fdt(machine); + + /* Pack resulting tree */ + _FDT((fdt_pack(fdt))); + + cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); +} + +/* If we don't use the built-in LPC interrupt deserializer, we need + * to provide a set of qirqs for the ISA bus or things will go bad. + * + * Most machines using pre-Naples chips (without said deserializer) + * have a CPLD that will collect the SerIRQ and shoot them as a + * single level interrupt to the P8 chip. So let's setup a hook + * for doing just that. + * + * Note: The actual interrupt input isn't emulated yet, this will + * come with the PSI bridge model. + */ +static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) +{ + /* We don't yet emulate the PSI bridge which provides the external + * interrupt, so just drop interrupts on the floor + */ +} + +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) +{ + /* XXX TODO */ +} + +static ISABus *pnv_isa_create(PnvChip *chip) +{ + PnvLpcController *lpc = &chip->lpc; + ISABus *isa_bus; + qemu_irq *irqs; + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + /* let isa_bus_new() create its own bridge on SysBus otherwise + * devices speficied on the command line won't find the bus and + * will fail to create. + */ + isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, + &error_fatal); + + /* Not all variants have a working serial irq decoder. If not, + * handling of LPC interrupts becomes a platform issue (some + * platforms have a CPLD to do it). + */ + if (pcc->chip_type == PNV_CHIP_POWER8NVL) { + irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS); + } else { + irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip, + ISA_NUM_IRQS); + } + + isa_bus_irqs(isa_bus, irqs); + return isa_bus; +} + +static void ppc_powernv_init(MachineState *machine) +{ + PnvMachineState *pnv = POWERNV_MACHINE(machine); + MemoryRegion *ram; + char *fw_filename; + long fw_size; + int i; + char *chip_typename; + + /* allocate RAM */ + if (machine->ram_size < (1 * G_BYTE)) { + error_report("Warning: skiboot may not work with < 1GB of RAM"); + } + + ram = g_new(MemoryRegion, 1); + memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), 0, ram); + + /* load skiboot firmware */ + if (bios_name == NULL) { + bios_name = FW_FILE_NAME; + } + + fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); + if (fw_size < 0) { + hw_error("qemu: could not load OPAL '%s'\n", fw_filename); + exit(1); + } + g_free(fw_filename); + + /* load kernel */ + if (machine->kernel_filename) { + long kernel_size; + + kernel_size = load_image_targphys(machine->kernel_filename, + KERNEL_LOAD_ADDR, 0x2000000); + if (kernel_size < 0) { + hw_error("qemu: could not load kernel'%s'\n", + machine->kernel_filename); + exit(1); + } + } + + /* load initrd */ + if (machine->initrd_filename) { + pnv->initrd_base = INITRD_LOAD_ADDR; + pnv->initrd_size = load_image_targphys(machine->initrd_filename, + pnv->initrd_base, 0x10000000); /* 128MB max */ + if (pnv->initrd_size < 0) { + error_report("qemu: could not load initial ram disk '%s'", + machine->initrd_filename); + exit(1); + } + } + + /* We need some cpu model to instantiate the PnvChip class */ + if (machine->cpu_model == NULL) { + machine->cpu_model = "POWER8"; + } + + /* Create the processor chips */ + chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model); + if (!object_class_by_name(chip_typename)) { + error_report("qemu: invalid CPU model '%s' for %s machine", + machine->cpu_model, MACHINE_GET_CLASS(machine)->name); + exit(1); + } + + pnv->chips = g_new0(PnvChip *, pnv->num_chips); + for (i = 0; i < pnv->num_chips; i++) { + char chip_name[32]; + Object *chip = object_new(chip_typename); + + pnv->chips[i] = PNV_CHIP(chip); + + /* TODO: put all the memory in one node on chip 0 until we find a + * way to specify different ranges for each chip + */ + if (i == 0) { + object_property_set_int(chip, machine->ram_size, "ram-size", + &error_fatal); + } + + snprintf(chip_name, sizeof(chip_name), "chip[%d]", PNV_CHIP_HWID(i)); + object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal); + object_property_set_int(chip, PNV_CHIP_HWID(i), "chip-id", + &error_fatal); + object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal); + object_property_set_bool(chip, true, "realized", &error_fatal); + } + g_free(chip_typename); + + /* Instantiate ISA bus on chip 0 */ + pnv->isa_bus = pnv_isa_create(pnv->chips[0]); + + /* Create serial port */ + serial_hds_isa_init(pnv->isa_bus, 0, MAX_SERIAL_PORTS); + + /* Create an RTC ISA device too */ + rtc_init(pnv->isa_bus, 2000, NULL); +} + +/* + * 0:21 Reserved - Read as zeros + * 22:24 Chip ID + * 25:28 Core number + * 29:31 Thread ID + */ +static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) +{ + return (chip->chip_id << 7) | (core_id << 3); +} + +/* + * 0:48 Reserved - Read as zeroes + * 49:52 Node ID + * 53:55 Chip ID + * 56 Reserved - Read as zero + * 57:61 Core number + * 62:63 Thread ID + * + * We only care about the lower bits. uint32_t is fine for the moment. + */ +static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) +{ + return (chip->chip_id << 8) | (core_id << 2); +} + +/* Allowed core identifiers on a POWER8 Processor Chip : + * + * <EX0 reserved> + * EX1 - Venice only + * EX2 - Venice only + * EX3 - Venice only + * EX4 + * EX5 + * EX6 + * <EX7,8 reserved> <reserved> + * EX9 - Venice only + * EX10 - Venice only + * EX11 - Venice only + * EX12 + * EX13 + * EX14 + * <EX15 reserved> + */ +#define POWER8E_CORE_MASK (0x7070ull) +#define POWER8_CORE_MASK (0x7e7eull) + +/* + * POWER9 has 24 cores, ids starting at 0x20 + */ +#define POWER9_CORE_MASK (0xffffff00000000ull) + +static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + + k->cpu_model = "POWER8E"; + k->chip_type = PNV_CHIP_POWER8E; + k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ + k->cores_mask = POWER8E_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p8; + k->xscom_base = 0x003fc0000000000ull; + dc->desc = "PowerNV Chip POWER8E"; +} + +static const TypeInfo pnv_chip_power8e_info = { + .name = TYPE_PNV_CHIP_POWER8E, + .parent = TYPE_PNV_CHIP, + .instance_size = sizeof(PnvChip), + .class_init = pnv_chip_power8e_class_init, +}; + +static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + + k->cpu_model = "POWER8"; + k->chip_type = PNV_CHIP_POWER8; + k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ + k->cores_mask = POWER8_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p8; + k->xscom_base = 0x003fc0000000000ull; + dc->desc = "PowerNV Chip POWER8"; +} + +static const TypeInfo pnv_chip_power8_info = { + .name = TYPE_PNV_CHIP_POWER8, + .parent = TYPE_PNV_CHIP, + .instance_size = sizeof(PnvChip), + .class_init = pnv_chip_power8_class_init, +}; + +static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + + k->cpu_model = "POWER8NVL"; + k->chip_type = PNV_CHIP_POWER8NVL; + k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ + k->cores_mask = POWER8_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p8; + k->xscom_base = 0x003fc0000000000ull; + dc->desc = "PowerNV Chip POWER8NVL"; +} + +static const TypeInfo pnv_chip_power8nvl_info = { + .name = TYPE_PNV_CHIP_POWER8NVL, + .parent = TYPE_PNV_CHIP, + .instance_size = sizeof(PnvChip), + .class_init = pnv_chip_power8nvl_class_init, +}; + +static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + + k->cpu_model = "POWER9"; + k->chip_type = PNV_CHIP_POWER9; + k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */ + k->cores_mask = POWER9_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p9; + k->xscom_base = 0x00603fc00000000ull; + dc->desc = "PowerNV Chip POWER9"; +} + +static const TypeInfo pnv_chip_power9_info = { + .name = TYPE_PNV_CHIP_POWER9, + .parent = TYPE_PNV_CHIP, + .instance_size = sizeof(PnvChip), + .class_init = pnv_chip_power9_class_init, +}; + +static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + int cores_max; + + /* + * No custom mask for this chip, let's use the default one from * + * the chip class + */ + if (!chip->cores_mask) { + chip->cores_mask = pcc->cores_mask; + } + + /* filter alien core ids ! some are reserved */ + if ((chip->cores_mask & pcc->cores_mask) != chip->cores_mask) { + error_setg(errp, "warning: invalid core mask for chip Ox%"PRIx64" !", + chip->cores_mask); + return; + } + chip->cores_mask &= pcc->cores_mask; + + /* now that we have a sane layout, let check the number of cores */ + cores_max = hweight_long(chip->cores_mask); + if (chip->nr_cores > cores_max) { + error_setg(errp, "warning: too many cores for chip ! Limit is %d", + cores_max); + return; + } +} + +static void pnv_chip_init(Object *obj) +{ + PnvChip *chip = PNV_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + chip->xscom_base = pcc->xscom_base; + + object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); + object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); +} + +static void pnv_chip_realize(DeviceState *dev, Error **errp) +{ + PnvChip *chip = PNV_CHIP(dev); + Error *error = NULL; + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + char *typename = pnv_core_typename(pcc->cpu_model); + size_t typesize = object_type_get_instance_size(typename); + int i, core_hwid; + + if (!object_class_by_name(typename)) { + error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename); + return; + } + + /* XSCOM bridge */ + pnv_xscom_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); + + /* Cores */ + pnv_chip_core_sanitize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + + chip->cores = g_malloc0(typesize * chip->nr_cores); + + for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8) + && (i < chip->nr_cores); core_hwid++) { + char core_name[32]; + void *pnv_core = chip->cores + i * typesize; + + if (!(chip->cores_mask & (1ull << core_hwid))) { + continue; + } + + object_initialize(pnv_core, typesize, typename); + snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid); + object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core), + &error_fatal); + object_property_set_int(OBJECT(pnv_core), smp_threads, "nr-threads", + &error_fatal); + object_property_set_int(OBJECT(pnv_core), core_hwid, + CPU_CORE_PROP_CORE_ID, &error_fatal); + object_property_set_int(OBJECT(pnv_core), + pcc->core_pir(chip, core_hwid), + "pir", &error_fatal); + object_property_set_bool(OBJECT(pnv_core), true, "realized", + &error_fatal); + object_unref(OBJECT(pnv_core)); + + /* Each core has an XSCOM MMIO region */ + pnv_xscom_add_subregion(chip, PNV_XSCOM_EX_CORE_BASE(core_hwid), + &PNV_CORE(pnv_core)->xscom_regs); + i++; + } + g_free(typename); + + /* Create LPC controller */ + object_property_set_bool(OBJECT(&chip->lpc), true, "realized", + &error_fatal); + pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); +} + +static Property pnv_chip_properties[] = { + DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0), + DEFINE_PROP_UINT64("ram-start", PnvChip, ram_start, 0), + DEFINE_PROP_UINT64("ram-size", PnvChip, ram_size, 0), + DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), + DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_chip_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_chip_realize; + dc->props = pnv_chip_properties; + dc->desc = "PowerNV Chip"; +} + +static const TypeInfo pnv_chip_info = { + .name = TYPE_PNV_CHIP, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = pnv_chip_class_init, + .instance_init = pnv_chip_init, + .class_size = sizeof(PnvChipClass), + .abstract = true, +}; + +static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint32(v, name, &POWERNV_MACHINE(obj)->num_chips, errp); +} + +static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PnvMachineState *pnv = POWERNV_MACHINE(obj); + uint32_t num_chips; + Error *local_err = NULL; + + visit_type_uint32(v, name, &num_chips, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * TODO: should we decide on how many chips we can create based + * on #cores and Venice vs. Murano vs. Naples chip type etc..., + */ + if (!is_power_of_2(num_chips) || num_chips > 4) { + error_setg(errp, "invalid number of chips: '%d'", num_chips); + return; + } + + pnv->num_chips = num_chips; +} + +static void powernv_machine_initfn(Object *obj) +{ + PnvMachineState *pnv = POWERNV_MACHINE(obj); + pnv->num_chips = 1; +} + +static void powernv_machine_class_props_init(ObjectClass *oc) +{ + object_class_property_add(oc, "num-chips", "uint32_t", + pnv_get_num_chips, pnv_set_num_chips, + NULL, NULL, NULL); + object_class_property_set_description(oc, "num-chips", + "Specifies the number of processor chips", + NULL); +} + +static void powernv_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "IBM PowerNV (Non-Virtualized)"; + mc->init = ppc_powernv_init; + mc->reset = ppc_powernv_reset; + mc->max_cpus = MAX_CPUS; + mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for + * storage */ + mc->no_parallel = 1; + mc->default_boot_order = NULL; + mc->default_ram_size = 1 * G_BYTE; + + powernv_machine_class_props_init(oc); +} + +static const TypeInfo powernv_machine_info = { + .name = TYPE_POWERNV_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(PnvMachineState), + .instance_init = powernv_machine_initfn, + .class_init = powernv_machine_class_init, +}; + +static void powernv_machine_register_types(void) +{ + type_register_static(&powernv_machine_info); + type_register_static(&pnv_chip_info); + type_register_static(&pnv_chip_power8e_info); + type_register_static(&pnv_chip_power8_info); + type_register_static(&pnv_chip_power8nvl_info); + type_register_static(&pnv_chip_power9_info); +} + +type_init(powernv_machine_register_types) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c new file mode 100644 index 0000000000..2acda9637d --- /dev/null +++ b/hw/ppc/pnv_core.c @@ -0,0 +1,232 @@ +/* + * QEMU PowerPC PowerNV CPU Core model + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "target-ppc/cpu.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_core.h" + +static void powernv_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + cpu_reset(cs); + + /* + * the skiboot firmware elects a primary thread to initialize the + * system and it can be any. + */ + env->gpr[3] = PNV_FDT_ADDR; + env->nip = 0x10; + env->msr |= MSR_HVB; /* Hypervisor mode */ +} + +static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp) +{ + CPUPPCState *env = &cpu->env; + int core_pir; + int thread_index = 0; /* TODO: TCG supports only one thread */ + ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; + + core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort); + + /* + * The PIR of a thread is the core PIR + the thread index. We will + * need to find a way to get the thread index when TCG supports + * more than 1. We could use the object name ? + */ + pir->default_value = core_pir + thread_index; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); + + qemu_register_reset(powernv_cpu_reset, cpu); +} + +/* + * These values are read by the PowerNV HW monitors under Linux + */ +#define PNV_XSCOM_EX_DTS_RESULT0 0x50000 +#define PNV_XSCOM_EX_DTS_RESULT1 0x50001 + +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = 0; + + /* The result should be 38 C */ + switch (offset) { + case PNV_XSCOM_EX_DTS_RESULT0: + val = 0x26f024f023f0000ull; + break; + case PNV_XSCOM_EX_DTS_RESULT1: + val = 0x24f000000000000ull; + break; + default: + qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx, + addr); + } + + return val; +} + +static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int width) +{ + qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx, + addr); +} + +static const MemoryRegionOps pnv_core_xscom_ops = { + .read = pnv_core_xscom_read, + .write = pnv_core_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_core_realize_child(Object *child, Error **errp) +{ + Error *local_err = NULL; + CPUState *cs = CPU(child); + PowerPCCPU *cpu = POWERPC_CPU(cs); + + object_property_set_bool(child, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + powernv_cpu_init(cpu, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void pnv_core_realize(DeviceState *dev, Error **errp) +{ + PnvCore *pc = PNV_CORE(OBJECT(dev)); + CPUCore *cc = CPU_CORE(OBJECT(dev)); + PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev)); + const char *typename = object_class_get_name(pcc->cpu_oc); + size_t size = object_type_get_instance_size(typename); + Error *local_err = NULL; + void *obj; + int i, j; + char name[32]; + + pc->threads = g_malloc0(size * cc->nr_threads); + for (i = 0; i < cc->nr_threads; i++) { + obj = pc->threads + i * size; + + object_initialize(obj, size, typename); + + snprintf(name, sizeof(name), "thread[%d]", i); + object_property_add_child(OBJECT(pc), name, obj, &local_err); + object_property_add_alias(obj, "core-pir", OBJECT(pc), + "pir", &local_err); + if (local_err) { + goto err; + } + object_unref(obj); + } + + for (j = 0; j < cc->nr_threads; j++) { + obj = pc->threads + j * size; + + pnv_core_realize_child(obj, &local_err); + if (local_err) { + goto err; + } + } + + snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id); + pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops, + pc, name, PNV_XSCOM_EX_CORE_SIZE); + return; + +err: + while (--i >= 0) { + obj = pc->threads + i * size; + object_unparent(obj); + } + g_free(pc->threads); + error_propagate(errp, local_err); +} + +static Property pnv_core_properties[] = { + DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_core_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PnvCoreClass *pcc = PNV_CORE_CLASS(oc); + + dc->realize = pnv_core_realize; + dc->props = pnv_core_properties; + pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data); +} + +static const TypeInfo pnv_core_info = { + .name = TYPE_PNV_CORE, + .parent = TYPE_CPU_CORE, + .instance_size = sizeof(PnvCore), + .class_size = sizeof(PnvCoreClass), + .abstract = true, +}; + +static const char *pnv_core_models[] = { + "POWER8E", "POWER8", "POWER8NVL", "POWER9" +}; + +static void pnv_core_register_types(void) +{ + int i ; + + type_register_static(&pnv_core_info); + for (i = 0; i < ARRAY_SIZE(pnv_core_models); ++i) { + TypeInfo ti = { + .parent = TYPE_PNV_CORE, + .instance_size = sizeof(PnvCore), + .class_init = pnv_core_class_init, + .class_data = (void *) pnv_core_models[i], + }; + ti.name = pnv_core_typename(pnv_core_models[i]); + type_register(&ti); + g_free((void *)ti.name); + } +} + +type_init(pnv_core_register_types) + +char *pnv_core_typename(const char *model) +{ + return g_strdup_printf(TYPE_PNV_CORE "-%s", model); +} diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c new file mode 100644 index 0000000000..00dbd8b07b --- /dev/null +++ b/hw/ppc/pnv_lpc.c @@ -0,0 +1,471 @@ +/* + * QEMU PowerPC PowerNV LPC controller + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "target-ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" + +#include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/fdt.h" + +#include <libfdt.h> + +enum { + ECCB_CTL = 0, + ECCB_RESET = 1, + ECCB_STAT = 2, + ECCB_DATA = 3, +}; + +/* OPB Master LS registers */ +#define OPB_MASTER_LS_IRQ_STAT 0x50 +#define OPB_MASTER_IRQ_LPC 0x00000800 +#define OPB_MASTER_LS_IRQ_MASK 0x54 +#define OPB_MASTER_LS_IRQ_POL 0x58 +#define OPB_MASTER_LS_IRQ_INPUT 0x5c + +/* LPC HC registers */ +#define LPC_HC_FW_SEG_IDSEL 0x24 +#define LPC_HC_FW_RD_ACC_SIZE 0x28 +#define LPC_HC_FW_RD_1B 0x00000000 +#define LPC_HC_FW_RD_2B 0x01000000 +#define LPC_HC_FW_RD_4B 0x02000000 +#define LPC_HC_FW_RD_16B 0x04000000 +#define LPC_HC_FW_RD_128B 0x07000000 +#define LPC_HC_IRQSER_CTRL 0x30 +#define LPC_HC_IRQSER_EN 0x80000000 +#define LPC_HC_IRQSER_QMODE 0x40000000 +#define LPC_HC_IRQSER_START_MASK 0x03000000 +#define LPC_HC_IRQSER_START_4CLK 0x00000000 +#define LPC_HC_IRQSER_START_6CLK 0x01000000 +#define LPC_HC_IRQSER_START_8CLK 0x02000000 +#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ +#define LPC_HC_IRQSTAT 0x38 +#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ +#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ +#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 +#define LPC_HC_IRQ_LRESET 0x00000400 +#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 +#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 +#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 +#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 +#define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 +#define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 +#define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 +#define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 +#define LPC_HC_ERROR_ADDRESS 0x40 + +#define LPC_OPB_SIZE 0x100000000ull + +#define ISA_IO_SIZE 0x00010000 +#define ISA_MEM_SIZE 0x10000000 +#define LPC_IO_OPB_ADDR 0xd0010000 +#define LPC_IO_OPB_SIZE 0x00010000 +#define LPC_MEM_OPB_ADDR 0xe0010000 +#define LPC_MEM_OPB_SIZE 0x10000000 +#define LPC_FW_OPB_ADDR 0xf0000000 +#define LPC_FW_OPB_SIZE 0x10000000 + +#define LPC_OPB_REGS_OPB_ADDR 0xc0010000 +#define LPC_OPB_REGS_OPB_SIZE 0x00002000 +#define LPC_HC_REGS_OPB_ADDR 0xc0012000 +#define LPC_HC_REGS_OPB_SIZE 0x00001000 + + +/* + * TODO: the "primary" cell should only be added on chip 0. This is + * how skiboot chooses the default LPC controller on multichip + * systems. + * + * It would be easly done if we can change the populate() interface to + * replace the PnvXScomInterface parameter by a PnvChip one + */ +static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) +{ + const char compat[] = "ibm,power8-lpc\0ibm,lpc"; + char *name; + int offset; + uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV_XSCOM_LPC_SIZE) + }; + + name = g_strdup_printf("isa@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); + return 0; +} + +/* + * These read/write handlers of the OPB address space should be common + * with the P9 LPC Controller which uses direct MMIOs. + * + * TODO: rework to use address_space_stq() and address_space_ldq() + * instead. + */ +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, + int sz) +{ + bool success; + + /* XXX Handle access size limits and FW read caching here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, false); + + return success; +} + +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, + int sz) +{ + bool success; + + /* XXX Handle access size limits here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, true); + + return success; +} + +#define ECCB_CTL_READ (1ull << (63 - 15)) +#define ECCB_CTL_SZ_LSH (63 - 7) +#define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH) +#define ECCB_CTL_ADDR_MASK 0xffffffffu; + +#define ECCB_STAT_OP_DONE (1ull << (63 - 52)) +#define ECCB_STAT_OP_ERR (1ull << (63 - 52)) +#define ECCB_STAT_RD_DATA_LSH (63 - 37) +#define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) + +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) +{ + /* XXX Check for magic bits at the top, addr size etc... */ + unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; + uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; + uint8_t data[4]; + bool success; + + if (cmd & ECCB_CTL_READ) { + success = opb_read(lpc, opb_addr, data, sz); + if (success) { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (((uint64_t)data[0]) << 24 | + ((uint64_t)data[1]) << 16 | + ((uint64_t)data[2]) << 8 | + ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; + } else { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (0xffffffffull << ECCB_STAT_RD_DATA_LSH); + } + } else { + data[0] = lpc->eccb_data_reg >> 24; + data[1] = lpc->eccb_data_reg >> 16; + data[2] = lpc->eccb_data_reg >> 8; + data[3] = lpc->eccb_data_reg; + + success = opb_write(lpc, opb_addr, data, sz); + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; + } + /* XXX Which error bit (if any) to signal OPB error ? */ +} + +static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset & 3) { + case ECCB_CTL: + case ECCB_RESET: + val = 0; + break; + case ECCB_STAT: + val = lpc->eccb_stat_reg; + lpc->eccb_stat_reg = 0; + break; + case ECCB_DATA: + val = ((uint64_t)lpc->eccb_data_reg) << 32; + break; + } + return val; +} + +static void pnv_lpc_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t offset = addr >> 3; + + switch (offset & 3) { + case ECCB_CTL: + pnv_lpc_do_eccb(lpc, val); + break; + case ECCB_RESET: + /* XXXX */ + break; + case ECCB_STAT: + break; + case ECCB_DATA: + lpc->eccb_data_reg = val >> 32; + break; + } +} + +static const MemoryRegionOps pnv_lpc_xscom_ops = { + .read = pnv_lpc_xscom_read, + .write = pnv_lpc_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + uint64_t val = 0xfffffffffffffffful; + + switch (addr) { + case LPC_HC_FW_SEG_IDSEL: + val = lpc->lpc_hc_fw_seg_idsel; + break; + case LPC_HC_FW_RD_ACC_SIZE: + val = lpc->lpc_hc_fw_rd_acc_size; + break; + case LPC_HC_IRQSER_CTRL: + val = lpc->lpc_hc_irqser_ctrl; + break; + case LPC_HC_IRQMASK: + val = lpc->lpc_hc_irqmask; + break; + case LPC_HC_IRQSTAT: + val = lpc->lpc_hc_irqstat; + break; + case LPC_HC_ERROR_ADDRESS: + val = lpc->lpc_hc_error_addr; + break; + default: + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } + return val; +} + +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PnvLpcController *lpc = opaque; + + /* XXX Filter out reserved bits */ + + switch (addr) { + case LPC_HC_FW_SEG_IDSEL: + /* XXX Actually figure out how that works as this impact + * memory regions/aliases + */ + lpc->lpc_hc_fw_seg_idsel = val; + break; + case LPC_HC_FW_RD_ACC_SIZE: + lpc->lpc_hc_fw_rd_acc_size = val; + break; + case LPC_HC_IRQSER_CTRL: + lpc->lpc_hc_irqser_ctrl = val; + break; + case LPC_HC_IRQMASK: + lpc->lpc_hc_irqmask = val; + break; + case LPC_HC_IRQSTAT: + lpc->lpc_hc_irqstat &= ~val; + break; + case LPC_HC_ERROR_ADDRESS: + break; + default: + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps lpc_hc_ops = { + .read = lpc_hc_read, + .write = lpc_hc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + uint64_t val = 0xfffffffffffffffful; + + switch (addr) { + case OPB_MASTER_LS_IRQ_STAT: + val = lpc->opb_irq_stat; + break; + case OPB_MASTER_LS_IRQ_MASK: + val = lpc->opb_irq_mask; + break; + case OPB_MASTER_LS_IRQ_POL: + val = lpc->opb_irq_pol; + break; + case OPB_MASTER_LS_IRQ_INPUT: + val = lpc->opb_irq_input; + break; + default: + qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } + + return val; +} + +static void opb_master_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = opaque; + + switch (addr) { + case OPB_MASTER_LS_IRQ_STAT: + lpc->opb_irq_stat &= ~val; + break; + case OPB_MASTER_LS_IRQ_MASK: + /* XXX Filter out reserved bits */ + lpc->opb_irq_mask = val; + break; + case OPB_MASTER_LS_IRQ_POL: + /* XXX Filter out reserved bits */ + lpc->opb_irq_pol = val; + break; + case OPB_MASTER_LS_IRQ_INPUT: + /* Read only */ + break; + default: + qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps opb_master_ops = { + .read = opb_master_read, + .write = opb_master_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void pnv_lpc_realize(DeviceState *dev, Error **errp) +{ + PnvLpcController *lpc = PNV_LPC(dev); + + /* Reg inits */ + lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; + + /* Create address space and backing MR for the OPB bus */ + memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); + address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); + + /* Create ISA IO and Mem space regions which are the root of + * the ISA bus (ie, ISA address spaces). We don't create a + * separate one for FW which we alias to memory. + */ + memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); + memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); + + /* Create windows from the OPB space to the ISA space */ + memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", + &lpc->isa_io, 0, LPC_IO_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, + &lpc->opb_isa_io); + memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem", + &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, + &lpc->opb_isa_mem); + memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", + &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, + &lpc->opb_isa_fw); + + /* Create MMIO regions for LPC HC and OPB registers */ + memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, + lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, + &lpc->opb_master_regs); + memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, + "lpc-hc", LPC_HC_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, + &lpc->lpc_hc_regs); + + /* XScom region for LPC registers */ + pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), + &pnv_lpc_xscom_ops, lpc, "xscom-lpc", + PNV_XSCOM_LPC_SIZE); +} + +static void pnv_lpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xdc->populate = pnv_lpc_populate; + + dc->realize = pnv_lpc_realize; +} + +static const TypeInfo pnv_lpc_info = { + .name = TYPE_PNV_LPC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvLpcController), + .class_init = pnv_lpc_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_lpc_register_types(void) +{ + type_register_static(&pnv_lpc_info); +} + +type_init(pnv_lpc_register_types) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c new file mode 100644 index 0000000000..5aaa264bd7 --- /dev/null +++ b/hw/ppc/pnv_xscom.c @@ -0,0 +1,275 @@ +/* + * QEMU PowerPC PowerNV XSCOM bus + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "qemu/log.h" +#include "sysemu/kvm.h" +#include "target-ppc/cpu.h" +#include "hw/sysbus.h" + +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv.h" + +#include <libfdt.h> + +static void xscom_complete(CPUState *cs, uint64_t hmer_bits) +{ + /* + * TODO: When the read/write comes from the monitor, NULL is + * passed for the cpu, and no CPU completion is generated. + */ + if (cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* + * TODO: Need a CPU helper to set HMER, also handle generation + * of HMIs + */ + cpu_synchronize_state(cs); + env->spr[SPR_HMER] |= hmer_bits; + } +} + +static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + addr &= (PNV_XSCOM_SIZE - 1); + if (pcc->chip_type == PNV_CHIP_POWER9) { + return addr >> 3; + } else { + return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); + } +} + +static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) +{ + switch (pcba) { + case 0xf000f: + return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id; + case 0x1010c00: /* PIBAM FIR */ + case 0x1010c03: /* PIBAM FIR MASK */ + case 0x2020007: /* ADU stuff */ + case 0x2020009: /* ADU stuff */ + case 0x202000f: /* ADU stuff */ + return 0; + case 0x2013f00: /* PBA stuff */ + case 0x2013f01: /* PBA stuff */ + case 0x2013f02: /* PBA stuff */ + case 0x2013f03: /* PBA stuff */ + case 0x2013f04: /* PBA stuff */ + case 0x2013f05: /* PBA stuff */ + case 0x2013f06: /* PBA stuff */ + case 0x2013f07: /* PBA stuff */ + return 0; + case 0x2013028: /* CAPP stuff */ + case 0x201302a: /* CAPP stuff */ + case 0x2013801: /* CAPP stuff */ + case 0x2013802: /* CAPP stuff */ + return 0; + default: + return -1; + } +} + +static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val) +{ + /* We ignore writes to these */ + switch (pcba) { + case 0xf000f: /* chip id is RO */ + case 0x1010c00: /* PIBAM FIR */ + case 0x1010c01: /* PIBAM FIR */ + case 0x1010c02: /* PIBAM FIR */ + case 0x1010c03: /* PIBAM FIR MASK */ + case 0x1010c04: /* PIBAM FIR MASK */ + case 0x1010c05: /* PIBAM FIR MASK */ + case 0x2020007: /* ADU stuff */ + case 0x2020009: /* ADU stuff */ + case 0x202000f: /* ADU stuff */ + return true; + default: + return false; + } +} + +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width) +{ + PnvChip *chip = opaque; + uint32_t pcba = pnv_xscom_pcba(chip, addr); + uint64_t val = 0; + MemTxResult result; + + /* Handle some SCOMs here before dispatch */ + val = xscom_read_default(chip, pcba); + if (val != -1) { + goto complete; + } + + val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED, + &result); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%" + HWADDR_PRIx " pcba=0x%08x\n", addr, pcba); + xscom_complete(current_cpu, HMER_XSCOM_FAIL | HMER_XSCOM_DONE); + return 0; + } + +complete: + xscom_complete(current_cpu, HMER_XSCOM_DONE); + return val; +} + +static void xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + PnvChip *chip = opaque; + uint32_t pcba = pnv_xscom_pcba(chip, addr); + MemTxResult result; + + /* Handle some SCOMs here before dispatch */ + if (xscom_write_default(chip, pcba, val)) { + goto complete; + } + + address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED, + &result); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%" + HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n", + addr, pcba, val); + xscom_complete(current_cpu, HMER_XSCOM_FAIL | HMER_XSCOM_DONE); + return; + } + +complete: + xscom_complete(current_cpu, HMER_XSCOM_DONE); +} + +const MemoryRegionOps pnv_xscom_ops = { + .read = xscom_read, + .write = xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +void pnv_xscom_realize(PnvChip *chip, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(chip); + char *name; + + name = g_strdup_printf("xscom-%x", chip->chip_id); + memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops, + chip, name, PNV_XSCOM_SIZE); + sysbus_init_mmio(sbd, &chip->xscom_mmio); + + memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE); + address_space_init(&chip->xscom_as, &chip->xscom, name); + g_free(name); +} + +static const TypeInfo pnv_xscom_interface_info = { + .name = TYPE_PNV_XSCOM_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(PnvXScomInterfaceClass), +}; + +static void pnv_xscom_register_types(void) +{ + type_register_static(&pnv_xscom_interface_info); +} + +type_init(pnv_xscom_register_types) + +typedef struct ForeachPopulateArgs { + void *fdt; + int xscom_offset; +} ForeachPopulateArgs; + +static int xscom_populate_child(Object *child, void *opaque) +{ + if (object_dynamic_cast(child, TYPE_PNV_XSCOM_INTERFACE)) { + ForeachPopulateArgs *args = opaque; + PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child); + PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd); + + if (xc->populate) { + _FDT((xc->populate(xd, args->fdt, args->xscom_offset))); + } + } + return 0; +} + +static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom"; +static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom"; + +int pnv_xscom_populate(PnvChip *chip, void *fdt, int root_offset) +{ + uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)), + cpu_to_be64(PNV_XSCOM_SIZE) }; + int xscom_offset; + ForeachPopulateArgs args; + char *name; + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0])); + xscom_offset = fdt_add_subnode(fdt, root_offset, name); + _FDT(xscom_offset); + g_free(name); + _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id))); + _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1))); + _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg)))); + + if (pcc->chip_type == PNV_CHIP_POWER9) { + _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9, + sizeof(compat_p9)))); + } else { + _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8, + sizeof(compat_p8)))); + } + + _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); + + args.fdt = fdt; + args.xscom_offset = xscom_offset; + + object_child_foreach(OBJECT(chip), xscom_populate_child, &args); + return 0; +} + +void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, MemoryRegion *mr) +{ + memory_region_add_subregion(&chip->xscom, offset << 3, mr); +} + +void pnv_xscom_region_init(MemoryRegion *mr, + struct Object *owner, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size) +{ + memory_region_init_io(mr, owner, ops, opaque, name, size << 3); +} diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 4b2f07aecb..d01798f245 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -37,7 +37,6 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "hw/loader.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 91989f028b..0cbab24c91 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -37,7 +37,6 @@ #include "sysemu/block-backend.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" -#include "sysemu/device_tree.h" #include "kvm_ppc.h" #include "migration/migration.h" #include "mmu-hash64.h" @@ -271,205 +270,6 @@ static void add_str(GString *s, const gchar *s1) g_string_append_len(s, s1, strlen(s1) + 1); } -static void *spapr_create_fdt_skel(hwaddr initrd_base, - hwaddr initrd_size, - hwaddr kernel_size, - bool little_endian, - const char *kernel_cmdline, - uint32_t epow_irq) -{ - void *fdt; - uint32_t start_prop = cpu_to_be32(initrd_base); - uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); - GString *hypertas = g_string_sized_new(256); - GString *qemu_hypertas = g_string_sized_new(256); - uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)}; - uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(max_cpus)}; - unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80}; - char *buf; - - add_str(hypertas, "hcall-pft"); - add_str(hypertas, "hcall-term"); - add_str(hypertas, "hcall-dabr"); - add_str(hypertas, "hcall-interrupt"); - add_str(hypertas, "hcall-tce"); - add_str(hypertas, "hcall-vio"); - add_str(hypertas, "hcall-splpar"); - add_str(hypertas, "hcall-bulk"); - add_str(hypertas, "hcall-set-mode"); - add_str(hypertas, "hcall-sprg0"); - add_str(hypertas, "hcall-copy"); - add_str(hypertas, "hcall-debug"); - add_str(qemu_hypertas, "hcall-memop1"); - - fdt = g_malloc0(FDT_MAX_SIZE); - _FDT((fdt_create(fdt, FDT_MAX_SIZE))); - - if (kernel_size) { - _FDT((fdt_add_reservemap_entry(fdt, KERNEL_LOAD_ADDR, kernel_size))); - } - if (initrd_size) { - _FDT((fdt_add_reservemap_entry(fdt, initrd_base, initrd_size))); - } - _FDT((fdt_finish_reservemap(fdt))); - - /* Root node */ - _FDT((fdt_begin_node(fdt, ""))); - _FDT((fdt_property_string(fdt, "device_type", "chrp"))); - _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 = qemu_uuid_unparse_strdup(&qemu_uuid); - - _FDT((fdt_property_string(fdt, "vm,uuid", buf))); - if (qemu_uuid_set) { - _FDT((fdt_property_string(fdt, "system-id", buf))); - } - g_free(buf); - - if (qemu_get_vm_name()) { - _FDT((fdt_property_string(fdt, "ibm,partition-name", - qemu_get_vm_name()))); - } - - _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); - _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); - - /* /chosen */ - _FDT((fdt_begin_node(fdt, "chosen"))); - - /* Set Form1_affinity */ - _FDT((fdt_property(fdt, "ibm,architecture-vec-5", vec5, sizeof(vec5)))); - - _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline))); - _FDT((fdt_property(fdt, "linux,initrd-start", - &start_prop, sizeof(start_prop)))); - _FDT((fdt_property(fdt, "linux,initrd-end", - &end_prop, sizeof(end_prop)))); - if (kernel_size) { - uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR), - cpu_to_be64(kernel_size) }; - - _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); - if (little_endian) { - _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0))); - } - } - if (boot_menu) { - _FDT((fdt_property_cell(fdt, "qemu,boot-menu", boot_menu))); - } - _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width))); - _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height))); - _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth))); - - _FDT((fdt_end_node(fdt))); - - /* RTAS */ - _FDT((fdt_begin_node(fdt, "rtas"))); - - if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { - add_str(hypertas, "hcall-multi-tce"); - } - _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas->str, - hypertas->len))); - g_string_free(hypertas, TRUE); - _FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas->str, - qemu_hypertas->len))); - g_string_free(qemu_hypertas, TRUE); - - _FDT((fdt_property(fdt, "ibm,associativity-reference-points", - refpoints, sizeof(refpoints)))); - - _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX))); - _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate", - RTAS_EVENT_SCAN_RATE))); - - if (msi_nonbroken) { - _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0))); - } - - /* - * According to PAPR, rtas ibm,os-term does not guarantee 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 */ - _FDT((fdt_begin_node(fdt, "interrupt-controller"))); - - _FDT((fdt_property_string(fdt, "device_type", - "PowerPC-External-Interrupt-Presentation"))); - _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"))); - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", - interrupt_server_ranges_prop, - sizeof(interrupt_server_ranges_prop)))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); - _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP))); - _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP))); - - _FDT((fdt_end_node(fdt))); - - /* vdevice */ - _FDT((fdt_begin_node(fdt, "vdevice"))); - - _FDT((fdt_property_string(fdt, "device_type", "vdevice"))); - _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice"))); - _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); - _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2))); - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - - _FDT((fdt_end_node(fdt))); - - /* event-sources */ - spapr_events_fdt_skel(fdt, epow_irq); - - /* /hypervisor node */ - if (kvm_enabled()) { - uint8_t hypercall[16]; - - /* indicate KVM hypercall interface */ - _FDT((fdt_begin_node(fdt, "hypervisor"))); - _FDT((fdt_property_string(fdt, "compatible", "linux,kvm"))); - if (kvmppc_has_cap_fixup_hcalls()) { - /* - * Older KVM versions with older guest kernels were broken with the - * magic page, don't allow the guest to map it. - */ - if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, - sizeof(hypercall))) { - _FDT((fdt_property(fdt, "hcall-instructions", hypercall, - sizeof(hypercall)))); - } - } - _FDT((fdt_end_node(fdt))); - } - - _FDT((fdt_end_node(fdt))); /* close root node */ - _FDT((fdt_finish(fdt))); - - return fdt; -} - static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, hwaddr size) { @@ -854,13 +654,42 @@ out: return ret; } +static int spapr_dt_cas_updates(sPAPRMachineState *spapr, void *fdt, + sPAPROptionVector *ov5_updates) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + int ret = 0, offset; + + /* Generate ibm,dynamic-reconfiguration-memory node if required */ + if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) { + g_assert(smc->dr_lmb_enabled); + ret = spapr_populate_drconf_memory(spapr, fdt); + if (ret) { + goto out; + } + } + + offset = fdt_path_offset(fdt, "/chosen"); + if (offset < 0) { + offset = fdt_add_subnode(fdt, 0, "chosen"); + if (offset < 0) { + return offset; + } + } + ret = spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas, + "ibm,architecture-vec-5"); + +out: + return ret; +} + int spapr_h_cas_compose_response(sPAPRMachineState *spapr, target_ulong addr, target_ulong size, - bool cpu_update, bool memory_update) + bool cpu_update, + sPAPROptionVector *ov5_updates) { void *fdt, *fdt_skel; sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine()); size -= sizeof(hdr); @@ -879,9 +708,8 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, _FDT((spapr_fixup_cpu_dt(fdt, spapr))); } - /* Generate ibm,dynamic-reconfiguration-memory node if required */ - if (memory_update && smc->dr_lmb_enabled) { - _FDT((spapr_populate_drconf_memory(spapr, fdt))); + if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) { + return -1; } /* Pack resulting tree */ @@ -900,25 +728,206 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, return 0; } -static void spapr_finalize_fdt(sPAPRMachineState *spapr, - hwaddr fdt_addr, - hwaddr rtas_addr, - hwaddr rtas_size) +static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) +{ + int rtas; + GString *hypertas = g_string_sized_new(256); + GString *qemu_hypertas = g_string_sized_new(256); + uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4) }; + uint64_t max_hotplug_addr = spapr->hotplug_memory.base + + memory_region_size(&spapr->hotplug_memory.mr); + uint32_t lrdr_capacity[] = { + cpu_to_be32(max_hotplug_addr >> 32), + cpu_to_be32(max_hotplug_addr & 0xffffffff), + 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE), + cpu_to_be32(max_cpus / smp_threads), + }; + + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); + + /* hypertas */ + add_str(hypertas, "hcall-pft"); + add_str(hypertas, "hcall-term"); + add_str(hypertas, "hcall-dabr"); + add_str(hypertas, "hcall-interrupt"); + add_str(hypertas, "hcall-tce"); + add_str(hypertas, "hcall-vio"); + add_str(hypertas, "hcall-splpar"); + add_str(hypertas, "hcall-bulk"); + add_str(hypertas, "hcall-set-mode"); + add_str(hypertas, "hcall-sprg0"); + add_str(hypertas, "hcall-copy"); + add_str(hypertas, "hcall-debug"); + add_str(qemu_hypertas, "hcall-memop1"); + + if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { + add_str(hypertas, "hcall-multi-tce"); + } + _FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions", + hypertas->str, hypertas->len)); + g_string_free(hypertas, TRUE); + _FDT(fdt_setprop(fdt, rtas, "qemu,hypertas-functions", + qemu_hypertas->str, qemu_hypertas->len)); + g_string_free(qemu_hypertas, TRUE); + + _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", + refpoints, sizeof(refpoints))); + + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", + RTAS_ERROR_LOG_MAX)); + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", + RTAS_EVENT_SCAN_RATE)); + + if (msi_nonbroken) { + _FDT(fdt_setprop(fdt, rtas, "ibm,change-msix-capable", NULL, 0)); + } + + /* + * According to PAPR, rtas ibm,os-term does not guarantee 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_setprop(fdt, rtas, "ibm,extended-os-term", NULL, 0)); + + _FDT(fdt_setprop(fdt, rtas, "ibm,lrdr-capacity", + lrdr_capacity, sizeof(lrdr_capacity))); + + spapr_dt_rtas_tokens(fdt, rtas); +} + +static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int chosen; + const char *boot_device = machine->boot_order; + char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + size_t cb = 0; + char *bootlist = get_boot_devices_list(&cb, true); + + _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); + + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", machine->kernel_cmdline)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", + spapr->initrd_base)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", + spapr->initrd_base + spapr->initrd_size)); + + if (spapr->kernel_size) { + uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR), + cpu_to_be64(spapr->kernel_size) }; + + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", + &kprop, sizeof(kprop))); + if (spapr->kernel_le) { + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + } + } + if (boot_menu) { + _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); + } + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); + + if (cb && bootlist) { + int i; + + for (i = 0; i < cb; i++) { + if (bootlist[i] == '\n') { + bootlist[i] = ' '; + } + } + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); + } + + if (boot_device && strlen(boot_device)) { + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); + } + + if (!spapr->has_graphics && stdout_path) { + _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + } + + g_free(stdout_path); + g_free(bootlist); +} + +static void spapr_dt_hypervisor(sPAPRMachineState *spapr, void *fdt) +{ + /* The /hypervisor node isn't in PAPR - this is a hack to allow PR + * KVM to work under pHyp with some guest co-operation */ + int hypervisor; + uint8_t hypercall[16]; + + _FDT(hypervisor = fdt_add_subnode(fdt, 0, "hypervisor")); + /* indicate KVM hypercall interface */ + _FDT(fdt_setprop_string(fdt, hypervisor, "compatible", "linux,kvm")); + if (kvmppc_has_cap_fixup_hcalls()) { + /* + * Older KVM versions with older guest kernels were broken + * with the magic page, don't allow the guest to map it. + */ + if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + sizeof(hypercall))) { + _FDT(fdt_setprop(fdt, hypervisor, "hcall-instructions", + hypercall, sizeof(hypercall))); + } + } +} + +static void *spapr_build_fdt(sPAPRMachineState *spapr, + hwaddr rtas_addr, + hwaddr rtas_size) { MachineState *machine = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); - const char *boot_device = machine->boot_order; - int ret, i; - size_t cb = 0; - char *bootlist; + int ret; void *fdt; sPAPRPHBState *phb; + char *buf; + + fdt = g_malloc0(FDT_MAX_SIZE); + _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + + /* Root node */ + _FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp")); + _FDT(fdt_setprop_string(fdt, 0, "model", "IBM pSeries (emulated by qemu)")); + _FDT(fdt_setprop_string(fdt, 0, "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_setprop_string(fdt, 0, "host-model", buf)); + g_free(buf); + } + if (kvmppc_get_host_serial(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); + g_free(buf); + } + + buf = qemu_uuid_unparse_strdup(&qemu_uuid); + + _FDT(fdt_setprop_string(fdt, 0, "vm,uuid", buf)); + if (qemu_uuid_set) { + _FDT(fdt_setprop_string(fdt, 0, "system-id", buf)); + } + g_free(buf); + + if (qemu_get_vm_name()) { + _FDT(fdt_setprop_string(fdt, 0, "ibm,partition-name", + qemu_get_vm_name())); + } - fdt = g_malloc(FDT_MAX_SIZE); + _FDT(fdt_setprop_cell(fdt, 0, "#address-cells", 2)); + _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); - /* open out the base tree into a temp buffer for the final tweaks */ - _FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE))); + /* /interrupt controller */ + spapr_dt_xics(spapr->xics, fdt, PHANDLE_XICP); ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { @@ -926,11 +935,8 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, exit(1); } - ret = spapr_populate_vdevice(spapr->vio_bus, fdt); - if (ret < 0) { - error_report("couldn't setup vio devices in fdt"); - exit(1); - } + /* /vdevice */ + spapr_dt_vdevice(spapr->vio_bus, fdt); if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { ret = spapr_rng_populate_dt(fdt); @@ -948,43 +954,9 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, } } - /* RTAS */ - ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size); - if (ret < 0) { - error_report("Couldn't set up RTAS device tree properties"); - } - /* cpus */ spapr_populate_cpus_dt_node(fdt, spapr); - bootlist = get_boot_devices_list(&cb, true); - if (cb && bootlist) { - int offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - exit(1); - } - for (i = 0; i < cb; i++) { - if (bootlist[i] == '\n') { - bootlist[i] = ' '; - } - - } - ret = fdt_setprop_string(fdt, offset, "qemu,boot-list", bootlist); - } - - if (boot_device && strlen(boot_device)) { - int offset = fdt_path_offset(fdt, "/chosen"); - - if (offset < 0) { - exit(1); - } - fdt_setprop_string(fdt, offset, "qemu,boot-device", boot_device); - } - - if (!spapr->has_graphics) { - spapr_populate_chosen_stdout(fdt, spapr->vio_bus); - } - if (smc->dr_lmb_enabled) { _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); } @@ -999,19 +971,36 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, } } - _FDT((fdt_pack(fdt))); + /* /event-sources */ + spapr_dt_events(spapr, fdt); - if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { - error_report("FDT too big ! 0x%x bytes (max is 0x%x)", - fdt_totalsize(fdt), FDT_MAX_SIZE); - exit(1); + /* /rtas */ + spapr_dt_rtas(spapr, fdt); + + /* /chosen */ + spapr_dt_chosen(spapr, fdt); + + /* /hypervisor */ + if (kvm_enabled()) { + spapr_dt_hypervisor(spapr, fdt); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); - cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + /* Build memory reserve map */ + if (spapr->kernel_size) { + _FDT((fdt_add_mem_rsv(fdt, KERNEL_LOAD_ADDR, spapr->kernel_size))); + } + if (spapr->initrd_size) { + _FDT((fdt_add_mem_rsv(fdt, spapr->initrd_base, spapr->initrd_size))); + } - g_free(bootlist); - g_free(fdt); + /* ibm,client-architecture-support updates */ + ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas); + if (ret < 0) { + error_report("couldn't setup CAS properties fdt"); + exit(1); + } + + return fdt; } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) @@ -1147,6 +1136,9 @@ static void ppc_spapr_reset(void) sPAPRMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; + hwaddr rtas_addr, fdt_addr; + void *fdt; + int rc; /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); @@ -1170,24 +1162,44 @@ static void ppc_spapr_reset(void) * 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; + rtas_addr = rtas_limit - RTAS_MAX_SIZE; + fdt_addr = rtas_addr - FDT_MAX_SIZE; - /* Load the fdt */ - spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr, - spapr->rtas_size); + /* if this reset wasn't generated by CAS, we should reset our + * negotiated options and start from scratch */ + if (!spapr->cas_reboot) { + spapr_ovec_cleanup(spapr->ov5_cas); + spapr->ov5_cas = spapr_ovec_new(); + } + + fdt = spapr_build_fdt(spapr, rtas_addr, spapr->rtas_size); + + spapr_load_rtas(spapr, fdt, rtas_addr); - /* Copy RTAS over */ - cpu_physical_memory_write(spapr->rtas_addr, spapr->rtas_blob, - spapr->rtas_size); + rc = fdt_pack(fdt); + + /* Should only fail if we've built a corrupted tree */ + assert(rc == 0); + + if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { + error_report("FDT too big ! 0x%x bytes (max is 0x%x)", + fdt_totalsize(fdt), FDT_MAX_SIZE); + exit(1); + } + + /* Load the fdt */ + qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); + cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + g_free(fdt); /* Set up the entry state */ first_ppc_cpu = POWERPC_CPU(first_cpu); - first_ppc_cpu->env.gpr[3] = spapr->fdt_addr; + first_ppc_cpu->env.gpr[3] = fdt_addr; first_ppc_cpu->env.gpr[5] = 0; first_cpu->halted = 0; first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT; + spapr->cas_reboot = false; } static void spapr_create_nvram(sPAPRMachineState *spapr) @@ -1682,7 +1694,6 @@ static void ppc_spapr_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; PCIHostState *phb; int i; @@ -1692,10 +1703,7 @@ static void ppc_spapr_init(MachineState *machine) void *rma = NULL; hwaddr rma_alloc_size; hwaddr node0_size = spapr_node0_size(); - uint32_t initrd_base = 0; - long kernel_size = 0, initrd_size = 0; long load_limit, fw_size; - bool kernel_le = false; char *filename; int smt = kvmppc_smt_threads(); int spapr_cores = smp_cpus / smp_threads; @@ -1769,10 +1777,22 @@ static void ppc_spapr_init(MachineState *machine) DIV_ROUND_UP(max_cpus * smt, smp_threads), XICS_IRQS_SPAPR, &error_fatal); + /* Set up containers for ibm,client-set-architecture negotiated options */ + spapr->ov5 = spapr_ovec_new(); + spapr->ov5_cas = spapr_ovec_new(); + if (smc->dr_lmb_enabled) { + spapr_ovec_set(spapr->ov5, OV5_DRCONF_MEMORY); spapr_validate_node_memory(machine, &error_fatal); } + spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); + + /* advertise support for dedicated HP event source to guests */ + if (spapr->use_hotplug_event_source) { + spapr_ovec_set(spapr->ov5, OV5_HP_EVT); + } + /* init CPUs */ if (machine->cpu_model == NULL) { machine->cpu_model = kvm_enabled() ? "host" : smc->tcg_default_cpu; @@ -1896,7 +1916,7 @@ static void ppc_spapr_init(MachineState *machine) } g_free(filename); - /* Set up EPOW events infrastructure */ + /* Set up RTAS event infrastructure */ spapr_events_init(spapr); /* Set up the RTC RTAS interfaces */ @@ -1968,19 +1988,19 @@ static void ppc_spapr_init(MachineState *machine) if (kernel_filename) { uint64_t lowaddr = 0; - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, - 0, 0); - if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { - kernel_size = load_elf(kernel_filename, - translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, - 0, 0); - kernel_le = kernel_size > 0; - } - if (kernel_size < 0) { - error_report("error loading %s: %s", - kernel_filename, load_elf_strerror(kernel_size)); + spapr->kernel_size = load_elf(kernel_filename, translate_kernel_address, + NULL, NULL, &lowaddr, NULL, 1, + PPC_ELF_MACHINE, 0, 0); + if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) { + spapr->kernel_size = load_elf(kernel_filename, + translate_kernel_address, NULL, NULL, + &lowaddr, NULL, 0, PPC_ELF_MACHINE, + 0, 0); + spapr->kernel_le = spapr->kernel_size > 0; + } + if (spapr->kernel_size < 0) { + error_report("error loading %s: %s", kernel_filename, + load_elf_strerror(spapr->kernel_size)); exit(1); } @@ -1989,17 +2009,17 @@ static void ppc_spapr_init(MachineState *machine) /* Try to locate the initrd in the gap between the kernel * and the firmware. Add a bit of space just in case */ - initrd_base = (KERNEL_LOAD_ADDR + kernel_size + 0x1ffff) & ~0xffff; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - load_limit - initrd_base); - if (initrd_size < 0) { + spapr->initrd_base = (KERNEL_LOAD_ADDR + spapr->kernel_size + + 0x1ffff) & ~0xffff; + spapr->initrd_size = load_image_targphys(initrd_filename, + spapr->initrd_base, + load_limit + - spapr->initrd_base); + if (spapr->initrd_size < 0) { error_report("could not load initial ram disk '%s'", initrd_filename); exit(1); } - } else { - initrd_base = 0; - initrd_size = 0; } } @@ -2025,13 +2045,6 @@ static void ppc_spapr_init(MachineState *machine) register_savevm_live(NULL, "spapr/htab", -1, 1, &savevm_htab_handlers, spapr); - /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, - kernel_size, kernel_le, - kernel_cmdline, - spapr->check_exception_irq); - assert(spapr->fdt_skel != NULL); - /* used by RTAS */ QTAILQ_INIT(&spapr->ccs_list); qemu_register_reset(spapr_ccs_reset_hook, spapr); @@ -2129,16 +2142,41 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) spapr->kvm_type = g_strdup(value); } +static bool spapr_get_modern_hotplug_events(Object *obj, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + return spapr->use_hotplug_event_source; +} + +static void spapr_set_modern_hotplug_events(Object *obj, bool value, + Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + spapr->use_hotplug_event_source = value; +} + static void spapr_machine_initfn(Object *obj) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); spapr->htab_fd = -1; + spapr->use_hotplug_event_source = true; object_property_add_str(obj, "kvm-type", spapr_get_kvm_type, spapr_set_kvm_type, NULL); object_property_set_description(obj, "kvm-type", "Specifies the KVM virtualization mode (HV, PR)", NULL); + object_property_add_bool(obj, "modern-hotplug-events", + spapr_get_modern_hotplug_events, + spapr_set_modern_hotplug_events, + NULL); + object_property_set_description(obj, "modern-hotplug-events", + "Use dedicated hotplug event mechanism in" + " place of standard EPOW events when possible" + " (required for memory hot-unplug support)", + NULL); } static void spapr_machine_finalizefn(Object *obj) @@ -2163,14 +2201,16 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, - uint32_t node, Error **errp) +static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, + uint32_t node, bool dedicated_hp_event_source, + Error **errp) { sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; int i, fdt_offset, fdt_size; void *fdt; + uint64_t addr = addr_start; for (i = 0; i < nr_lmbs; i++) { drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, @@ -2189,7 +2229,17 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, * guest only in case of hotplugged memory */ if (dev->hotplugged) { - spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); + if (dedicated_hp_event_source) { + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr_start / SPAPR_MEMORY_BLOCK_SIZE); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs, + drck->get_index(drc)); + } else { + spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs); + } } } @@ -2222,8 +2272,94 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - spapr_add_lmbs(dev, addr, size, node, &error_abort); + spapr_add_lmbs(dev, addr, size, node, + spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), + &error_abort); + +out: + error_propagate(errp, local_err); +} + +typedef struct sPAPRDIMMState { + uint32_t nr_lmbs; +} sPAPRDIMMState; + +static void spapr_lmb_release(DeviceState *dev, void *opaque) +{ + sPAPRDIMMState *ds = (sPAPRDIMMState *)opaque; + HotplugHandler *hotplug_ctrl; + + if (--ds->nr_lmbs) { + return; + } + + g_free(ds); + + /* + * Now that all the LMBs have been removed by the guest, call the + * pc-dimm unplug handler to cleanup up the pc-dimm device. + */ + hotplug_ctrl = qdev_get_hotplug_handler(dev); + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_del_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, + Error **errp) +{ + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; + int i; + sPAPRDIMMState *ds = g_malloc0(sizeof(sPAPRDIMMState)); + uint64_t addr = addr_start; + + ds->nr_lmbs = nr_lmbs; + for (i = 0; i < nr_lmbs; i++) { + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->detach(drc, dev, spapr_lmb_release, ds, errp); + addr += SPAPR_MEMORY_BLOCK_SIZE; + } + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr_start / SPAPR_MEMORY_BLOCK_SIZE); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs, + drck->get_index(drc)); +} + +static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + + pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + object_unparent(OBJECT(dev)); +} + +static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + Error *local_err = NULL; + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t size = memory_region_size(mr); + uint64_t addr; + addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + goto out; + } + + spapr_del_lmbs(dev, addr, size, &error_abort); out: error_propagate(errp, local_err); } @@ -2301,10 +2437,42 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - error_setg(errp, "Memory hot unplug not supported by sPAPR"); + if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { + spapr_memory_unplug(hotplug_dev, dev, errp); + } else { + error_setg(errp, "Memory hot unplug not supported for this guest"); + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + if (!mc->query_hotpluggable_cpus) { + error_setg(errp, "CPU hot unplug not supported on this machine"); + return; + } + spapr_core_unplug(hotplug_dev, dev, errp); + } +} + +static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { + spapr_memory_unplug_request(hotplug_dev, dev, errp); + } else { + /* NOTE: this means there is a window after guest reset, prior to + * CAS negotiation, where unplug requests will fail due to the + * capability not being detected yet. This is a bit different than + * the case with PCI unplug, where the events will be queued and + * eventually handled by the guest after boot + */ + error_setg(errp, "Memory hot unplug not supported for this guest"); + } } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { if (!mc->query_hotpluggable_cpus) { error_setg(errp, "CPU hot unplug not supported on this machine"); @@ -2450,6 +2618,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) hc->plug = spapr_machine_device_plug; hc->unplug = spapr_machine_device_unplug; mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id; + hc->unplug_request = spapr_machine_device_unplug_request; smc->dr_lmb_enabled = true; smc->tcg_default_cpu = "POWER8"; @@ -2585,7 +2754,10 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, static void spapr_machine_2_7_instance_options(MachineState *machine) { + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + spapr_machine_2_8_instance_options(machine); + spapr->use_hotplug_event_source = false; } static void spapr_machine_2_7_class_options(MachineClass *mc) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index bc922bc86f..e0c14f6b77 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -184,7 +184,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, /* * Setup CPU DT entries only for hotplugged CPUs. For boot time or - * coldplugged CPUs DT entries are setup in spapr_finalize_fdt(). + * coldplugged CPUs DT entries are setup in spapr_build_fdt(). */ if (dev->hotplugged) { fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 6e54fd4743..a0c44ee593 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -68,6 +68,23 @@ static uint32_t set_isolation_state(sPAPRDRConnector *drc, } } + /* + * Fail any requests to ISOLATE the LMB DRC if this LMB doesn't + * belong to a DIMM device that is marked for removal. + * + * Currently the guest userspace tool drmgr that drives the memory + * hotplug/unplug will just try to remove a set of 'removable' LMBs + * in response to a hot unplug request that is based on drc-count. + * If the LMB being removed doesn't belong to a DIMM device that is + * actually being unplugged, fail the isolation request here. + */ + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_LMB) { + if ((state == SPAPR_DR_ISOLATION_STATE_ISOLATED) && + !drc->awaiting_release) { + return RTAS_OUT_HW_ERROR; + } + } + drc->isolation_state = state; if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 6d3534541c..f85a9c32a7 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -40,6 +40,7 @@ #include "hw/ppc/spapr_drc.h" #include "qemu/help_option.h" #include "qemu/bcd.h" +#include "hw/ppc/spapr_ovec.h" #include <libfdt.h> struct rtas_error_log { @@ -174,6 +175,16 @@ struct epow_log_full { struct rtas_event_log_v6_epow epow; } QEMU_PACKED; +union drc_identifier { + uint32_t index; + uint32_t count; + struct { + uint32_t count; + uint32_t index; + } count_indexed; + char name[1]; +} QEMU_PACKED; + struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */ struct rtas_event_log_v6_section_header hdr; @@ -190,12 +201,9 @@ struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_HP_ID_DRC_NAME 1 #define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 #define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 +#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4 uint8_t reserved; - union { - uint32_t index; - uint32_t count; - char name[1]; - } drc; + union drc_identifier drc_id; } QEMU_PACKED; struct hp_log_full { @@ -206,28 +214,132 @@ struct hp_log_full { struct rtas_event_log_v6_hp hp; } QEMU_PACKED; -#define EVENT_MASK_INTERNAL_ERRORS 0x80000000 -#define EVENT_MASK_EPOW 0x40000000 -#define EVENT_MASK_HOTPLUG 0x10000000 -#define EVENT_MASK_IO 0x08000000 +typedef enum EventClass { + EVENT_CLASS_INTERNAL_ERRORS = 0, + EVENT_CLASS_EPOW = 1, + EVENT_CLASS_RESERVED = 2, + EVENT_CLASS_HOT_PLUG = 3, + EVENT_CLASS_IO = 4, + EVENT_CLASS_MAX +} EventClassIndex; +#define EVENT_CLASS_MASK(index) (1 << (31 - index)) + +static const char * const event_names[EVENT_CLASS_MAX] = { + [EVENT_CLASS_INTERNAL_ERRORS] = "internal-errors", + [EVENT_CLASS_EPOW] = "epow-events", + [EVENT_CLASS_HOT_PLUG] = "hot-plug-events", + [EVENT_CLASS_IO] = "ibm,io-events", +}; + +struct sPAPREventSource { + int irq; + uint32_t mask; + bool enabled; +}; + +static sPAPREventSource *spapr_event_sources_new(void) +{ + return g_new0(sPAPREventSource, EVENT_CLASS_MAX); +} + +static void spapr_event_sources_register(sPAPREventSource *event_sources, + EventClassIndex index, int irq) +{ + /* we only support 1 irq per event class at the moment */ + g_assert(event_sources); + g_assert(!event_sources[index].enabled); + event_sources[index].irq = irq; + event_sources[index].mask = EVENT_CLASS_MASK(index); + event_sources[index].enabled = true; +} + +static const sPAPREventSource * +spapr_event_sources_get_source(sPAPREventSource *event_sources, + EventClassIndex index) +{ + g_assert(index < EVENT_CLASS_MAX); + g_assert(event_sources); + + return &event_sources[index]; +} + +void spapr_dt_events(sPAPRMachineState *spapr, void *fdt) +{ + uint32_t irq_ranges[EVENT_CLASS_MAX * 2]; + int i, count = 0, event_sources; + sPAPREventSource *events = spapr->event_sources; + + g_assert(events); + + _FDT(event_sources = fdt_add_subnode(fdt, 0, "event-sources")); + + for (i = 0, count = 0; i < EVENT_CLASS_MAX; i++) { + int node_offset; + uint32_t interrupts[2]; + const sPAPREventSource *source = + spapr_event_sources_get_source(events, i); + const char *source_name = event_names[i]; + + if (!source->enabled) { + continue; + } + + interrupts[0] = cpu_to_be32(source->irq); + interrupts[1] = 0; -void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq) + _FDT(node_offset = fdt_add_subnode(fdt, event_sources, source_name)); + _FDT(fdt_setprop(fdt, node_offset, "interrupts", interrupts, + sizeof(interrupts))); + + irq_ranges[count++] = interrupts[0]; + irq_ranges[count++] = cpu_to_be32(1); + } + + irq_ranges[count] = cpu_to_be32(count); + count++; + + _FDT((fdt_setprop(fdt, event_sources, "interrupt-controller", NULL, 0))); + _FDT((fdt_setprop_cell(fdt, event_sources, "#interrupt-cells", 2))); + _FDT((fdt_setprop(fdt, event_sources, "interrupt-ranges", + irq_ranges, count * sizeof(uint32_t)))); +} + +static const sPAPREventSource * +rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type) { - uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)}; - uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0}; + const sPAPREventSource *source; + + g_assert(spapr->event_sources); + + switch (log_type) { + case RTAS_LOG_TYPE_HOTPLUG: + source = spapr_event_sources_get_source(spapr->event_sources, + EVENT_CLASS_HOT_PLUG); + if (spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)) { + g_assert(source->enabled); + break; + } + /* fall back to epow for legacy hotplug interrupt source */ + case RTAS_LOG_TYPE_EPOW: + source = spapr_event_sources_get_source(spapr->event_sources, + EVENT_CLASS_EPOW); + break; + default: + source = NULL; + } - _FDT((fdt_begin_node(fdt, "event-sources"))); + return source; +} - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); - _FDT((fdt_property(fdt, "interrupt-ranges", - irq_ranges, sizeof(irq_ranges)))); +static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type) +{ + const sPAPREventSource *source; - _FDT((fdt_begin_node(fdt, "epow-events"))); - _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts)))); - _FDT((fdt_end_node(fdt))); + source = rtas_event_log_to_source(spapr, log_type); + g_assert(source); + g_assert(source->enabled); - _FDT((fdt_end_node(fdt))); + return source->irq; } static void rtas_event_log_queue(int log_type, void *data, bool exception) @@ -248,19 +360,15 @@ static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask, sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); sPAPREventLogEntry *entry = NULL; - /* we only queue EPOW events atm. */ - if ((event_mask & EVENT_MASK_EPOW) == 0) { - return NULL; - } - QTAILQ_FOREACH(entry, &spapr->pending_events, next) { + const sPAPREventSource *source = + rtas_event_log_to_source(spapr, entry->log_type); + if (entry->exception != exception) { continue; } - /* EPOW and hotplug events are surfaced in the same manner */ - if (entry->log_type == RTAS_LOG_TYPE_EPOW || - entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { + if (source->mask & event_mask) { break; } } @@ -277,19 +385,15 @@ static bool rtas_event_log_contains(uint32_t event_mask, bool exception) sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); sPAPREventLogEntry *entry = NULL; - /* we only queue EPOW events atm. */ - if ((event_mask & EVENT_MASK_EPOW) == 0) { - return false; - } - QTAILQ_FOREACH(entry, &spapr->pending_events, next) { + const sPAPREventSource *source = + rtas_event_log_to_source(spapr, entry->log_type); + if (entry->exception != exception) { continue; } - /* EPOW and hotplug events are surfaced in the same manner */ - if (entry->log_type == RTAS_LOG_TYPE_EPOW || - entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { + if (source->mask & event_mask) { return true; } } @@ -377,7 +481,9 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true); - qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq)); + qemu_irq_pulse(xics_get_qirq(spapr->xics, + rtas_event_log_to_irq(spapr, + RTAS_LOG_TYPE_EPOW))); } static void spapr_hotplug_set_signalled(uint32_t drc_index) @@ -389,7 +495,7 @@ static void spapr_hotplug_set_signalled(uint32_t drc_index) static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, sPAPRDRConnectorType drc_type, - uint32_t drc) + union drc_identifier *drc_id) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); struct hp_log_full *new_hp; @@ -434,7 +540,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, case SPAPR_DR_CONNECTOR_TYPE_PCI: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI; if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) { - spapr_hotplug_set_signalled(drc); + spapr_hotplug_set_signalled(drc_id->index); } break; case SPAPR_DR_CONNECTOR_TYPE_LMB: @@ -452,48 +558,89 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, } if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) { - hp->drc.count = cpu_to_be32(drc); + hp->drc_id.count = cpu_to_be32(drc_id->count); } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) { - hp->drc.index = cpu_to_be32(drc); + hp->drc_id.index = cpu_to_be32(drc_id->index); + } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED) { + /* we should not be using count_indexed value unless the guest + * supports dedicated hotplug event source + */ + g_assert(spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)); + hp->drc_id.count_indexed.count = + cpu_to_be32(drc_id->count_indexed.count); + hp->drc_id.count_indexed.index = + cpu_to_be32(drc_id->count_indexed.index); } rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true); - qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq)); + qemu_irq_pulse(xics_get_qirq(spapr->xics, + rtas_event_log_to_irq(spapr, + RTAS_LOG_TYPE_HOTPLUG))); } void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); sPAPRDRConnectorType drc_type = drck->get_type(drc); - uint32_t index = drck->get_index(drc); + union drc_identifier drc_id; + drc_id.index = drck->get_index(drc); spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, - RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index); + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); sPAPRDRConnectorType drc_type = drck->get_type(drc); - uint32_t index = drck->get_index(drc); + union drc_identifier drc_id; + drc_id.index = drck->get_index(drc); spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, - RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index); + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, uint32_t count) { + union drc_identifier drc_id; + + drc_id.count = count; spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, - RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count); + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, uint32_t count) { + union drc_identifier drc_id; + + drc_id.count = count; spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, - RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count); + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); +} + +void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index) +{ + union drc_identifier drc_id; + + drc_id.count_indexed.count = count; + drc_id.count_indexed.index = index; + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED, + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); +} + +void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index) +{ + union drc_identifier drc_id; + + drc_id.count_indexed.count = count; + drc_id.count_indexed.index = index; + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED, + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -505,6 +652,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint64_t xinfo; sPAPREventLogEntry *event; struct rtas_error_log *hdr; + int i; if ((nargs < 6) || (nargs > 7) || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -541,8 +689,14 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, * do the latter here, since our code relies on edge-triggered * interrupts. */ - if (rtas_event_log_contains(mask, true)) { - qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq)); + for (i = 0; i < EVENT_CLASS_MAX; i++) { + if (rtas_event_log_contains(EVENT_CLASS_MASK(i), true)) { + const sPAPREventSource *source = + spapr_event_sources_get_source(spapr->event_sources, i); + + g_assert(source->enabled); + qemu_irq_pulse(xics_get_qirq(spapr->xics, source->irq)); + } } return; @@ -594,8 +748,27 @@ out_no_events: void spapr_events_init(sPAPRMachineState *spapr) { QTAILQ_INIT(&spapr->pending_events); - spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, false, - &error_fatal); + + spapr->event_sources = spapr_event_sources_new(); + + spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW, + xics_spapr_alloc(spapr->xics, 0, false, + &error_fatal)); + + /* NOTE: if machine supports modern/dedicated hotplug event source, + * we add it to the device-tree unconditionally. This means we may + * have cases where the source is enabled in QEMU, but unused by the + * guest because it does not support modern hotplug events, so we + * take care to rely on checking for negotiation of OV5_HP_EVT option + * before attempting to use it to signal events, rather than simply + * checking that it's enabled. + */ + if (spapr->use_hotplug_event_source) { + spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG, + xics_spapr_alloc(spapr->xics, 0, false, + &error_fatal)); + } + spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception", diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 682de40440..9a9bedf1bd 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -11,6 +11,7 @@ #include "trace.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" +#include "hw/ppc/spapr_ovec.h" struct SPRSyncState { int spr; @@ -880,32 +881,6 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -/* - * Return the offset to the requested option vector @vector in the - * option vector table @table. - */ -static target_ulong cas_get_option_vector(int vector, target_ulong table) -{ - int i; - char nr_vectors, nr_entries; - - if (!table) { - return 0; - } - - nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1; - if (!vector || vector > nr_vectors) { - return 0; - } - table++; /* skip nr option vectors */ - - for (i = 0; i < vector - 1; i++) { - nr_entries = ldl_phys(&address_space_memory, table) >> 24; - table += nr_entries + 2; - } - return table; -} - typedef struct { uint32_t cpu_version; Error *err; @@ -961,23 +936,21 @@ static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr, } } -#define OV5_DRCONF_MEMORY 0x20 - static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong list = ppc64_phys_to_real(args[0]); - target_ulong ov_table, ov5; + target_ulong ov_table; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_); CPUState *cs; - bool cpu_match = false, cpu_update = true, memory_update = false; + bool cpu_match = false, cpu_update = true; unsigned old_cpu_version = cpu_->cpu_version; unsigned compat_lvl = 0, cpu_version = 0; unsigned max_lvl = get_compat_level(cpu_->max_compat); int counter; - char ov5_byte2; + sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; /* Parse PVR list */ for (counter = 0; counter < 512; ++counter) { @@ -1033,19 +1006,34 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, /* For the future use: here @ov_table points to the first option vector */ ov_table = list; - ov5 = cas_get_option_vector(5, ov_table); - if (!ov5) { - return H_SUCCESS; - } + ov5_guest = spapr_ovec_parse_vector(ov_table, 5); + + /* NOTE: there are actually a number of ov5 bits where input from the + * guest is always zero, and the platform/QEMU enables them independently + * of guest input. To model these properly we'd want some sort of mask, + * but since they only currently apply to memory migration as defined + * by LoPAPR 1.1, 14.5.4.8, which QEMU doesn't implement, we don't need + * to worry about this for now. + */ + ov5_cas_old = spapr_ovec_clone(spapr->ov5_cas); + /* full range of negotiated ov5 capabilities */ + spapr_ovec_intersect(spapr->ov5_cas, spapr->ov5, ov5_guest); + spapr_ovec_cleanup(ov5_guest); + /* capabilities that have been added since CAS-generated guest reset. + * if capabilities have since been removed, generate another reset + */ + ov5_updates = spapr_ovec_new(); + spapr->cas_reboot = spapr_ovec_diff(ov5_updates, + ov5_cas_old, spapr->ov5_cas); - /* @list now points to OV 5 */ - ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2); - if (ov5_byte2 & OV5_DRCONF_MEMORY) { - memory_update = true; + if (!spapr->cas_reboot) { + spapr->cas_reboot = + (spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update, + ov5_updates) != 0); } + spapr_ovec_cleanup(ov5_updates); - if (spapr_h_cas_compose_response(spapr, args[1], args[2], - cpu_update, memory_update)) { + if (spapr->cas_reboot) { qemu_system_reset_request(); } diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c new file mode 100644 index 0000000000..c2a0d18577 --- /dev/null +++ b/hw/ppc/spapr_ovec.c @@ -0,0 +1,242 @@ +/* + * QEMU SPAPR Architecture Option Vector Helper Functions + * + * Copyright IBM Corp. 2016 + * + * Authors: + * Bharata B Rao <bharata@linux.vnet.ibm.com> + * Michael Roth <mdroth@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/ppc/spapr_ovec.h" +#include "qemu/bitmap.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" +#include <libfdt.h> + +/* #define DEBUG_SPAPR_OVEC */ + +#ifdef DEBUG_SPAPR_OVEC +#define DPRINTFN(fmt, ...) \ + do { fprintf(stderr, fmt "\n", ## __VA_ARGS__); } while (0) +#else +#define DPRINTFN(fmt, ...) \ + do { } while (0) +#endif + +#define OV_MAXBYTES 256 /* not including length byte */ +#define OV_MAXBITS (OV_MAXBYTES * BITS_PER_BYTE) + +/* we *could* work with bitmaps directly, but handling the bitmap privately + * allows us to more safely make assumptions about the bitmap size and + * simplify the calling code somewhat + */ +struct sPAPROptionVector { + unsigned long *bitmap; +}; + +sPAPROptionVector *spapr_ovec_new(void) +{ + sPAPROptionVector *ov; + + ov = g_new0(sPAPROptionVector, 1); + ov->bitmap = bitmap_new(OV_MAXBITS); + + return ov; +} + +sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig) +{ + sPAPROptionVector *ov; + + g_assert(ov_orig); + + ov = spapr_ovec_new(); + bitmap_copy(ov->bitmap, ov_orig->bitmap, OV_MAXBITS); + + return ov; +} + +void spapr_ovec_intersect(sPAPROptionVector *ov, + sPAPROptionVector *ov1, + sPAPROptionVector *ov2) +{ + g_assert(ov); + g_assert(ov1); + g_assert(ov2); + + bitmap_and(ov->bitmap, ov1->bitmap, ov2->bitmap, OV_MAXBITS); +} + +/* returns true if options bits were removed, false otherwise */ +bool spapr_ovec_diff(sPAPROptionVector *ov, + sPAPROptionVector *ov_old, + sPAPROptionVector *ov_new) +{ + unsigned long *change_mask = bitmap_new(OV_MAXBITS); + unsigned long *removed_bits = bitmap_new(OV_MAXBITS); + bool bits_were_removed = false; + + g_assert(ov); + g_assert(ov_old); + g_assert(ov_new); + + bitmap_xor(change_mask, ov_old->bitmap, ov_new->bitmap, OV_MAXBITS); + bitmap_and(ov->bitmap, ov_new->bitmap, change_mask, OV_MAXBITS); + bitmap_and(removed_bits, ov_old->bitmap, change_mask, OV_MAXBITS); + + if (!bitmap_empty(removed_bits, OV_MAXBITS)) { + bits_were_removed = true; + } + + g_free(change_mask); + g_free(removed_bits); + + return bits_were_removed; +} + +void spapr_ovec_cleanup(sPAPROptionVector *ov) +{ + if (ov) { + g_free(ov->bitmap); + g_free(ov); + } +} + +void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) +{ + g_assert(ov); + g_assert_cmpint(bitnr, <, OV_MAXBITS); + + set_bit(bitnr, ov->bitmap); +} + +void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) +{ + g_assert(ov); + g_assert_cmpint(bitnr, <, OV_MAXBITS); + + clear_bit(bitnr, ov->bitmap); +} + +bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr) +{ + g_assert(ov); + g_assert_cmpint(bitnr, <, OV_MAXBITS); + + return test_bit(bitnr, ov->bitmap) ? true : false; +} + +static void guest_byte_to_bitmap(uint8_t entry, unsigned long *bitmap, + long bitmap_offset) +{ + int i; + + for (i = 0; i < BITS_PER_BYTE; i++) { + if (entry & (1 << (BITS_PER_BYTE - 1 - i))) { + bitmap_set(bitmap, bitmap_offset + i, 1); + } + } +} + +static uint8_t guest_byte_from_bitmap(unsigned long *bitmap, long bitmap_offset) +{ + uint8_t entry = 0; + int i; + + for (i = 0; i < BITS_PER_BYTE; i++) { + if (test_bit(bitmap_offset + i, bitmap)) { + entry |= (1 << (BITS_PER_BYTE - 1 - i)); + } + } + + return entry; +} + +static target_ulong vector_addr(target_ulong table_addr, int vector) +{ + uint16_t vector_count, vector_len; + int i; + + vector_count = ldub_phys(&address_space_memory, table_addr) + 1; + if (vector > vector_count) { + return 0; + } + table_addr++; /* skip nr option vectors */ + + for (i = 0; i < vector - 1; i++) { + vector_len = ldub_phys(&address_space_memory, table_addr) + 1; + table_addr += vector_len + 1; /* bit-vector + length byte */ + } + return table_addr; +} + +sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) +{ + sPAPROptionVector *ov; + target_ulong addr; + uint16_t vector_len; + int i; + + g_assert(table_addr); + g_assert_cmpint(vector, >=, 1); /* vector numbering starts at 1 */ + + addr = vector_addr(table_addr, vector); + if (!addr) { + /* specified vector isn't present */ + return NULL; + } + + vector_len = ldub_phys(&address_space_memory, addr++) + 1; + g_assert_cmpint(vector_len, <=, OV_MAXBYTES); + ov = spapr_ovec_new(); + + for (i = 0; i < vector_len; i++) { + uint8_t entry = ldub_phys(&address_space_memory, addr + i); + if (entry) { + DPRINTFN("read guest vector %2d, byte %3d / %3d: 0x%.2x", + vector, i + 1, vector_len, entry); + guest_byte_to_bitmap(entry, ov->bitmap, i * BITS_PER_BYTE); + } + } + + return ov; +} + +int spapr_ovec_populate_dt(void *fdt, int fdt_offset, + sPAPROptionVector *ov, const char *name) +{ + uint8_t vec[OV_MAXBYTES + 1]; + uint16_t vec_len; + unsigned long lastbit; + int i; + + g_assert(ov); + + lastbit = find_last_bit(ov->bitmap, OV_MAXBITS); + /* if no bits are set, include at least 1 byte of the vector so we can + * still encoded this in the device tree while abiding by the same + * encoding/sizing expected in ibm,client-architecture-support + */ + vec_len = (lastbit == OV_MAXBITS) ? 1 : lastbit / BITS_PER_BYTE + 1; + g_assert_cmpint(vec_len, <=, OV_MAXBYTES); + /* guest expects vector len encoded as vec_len - 1, since the length byte + * is assumed and not included, and the first byte of the vector + * is assumed as well + */ + vec[0] = vec_len - 1; + + for (i = 1; i < vec_len + 1; i++) { + vec[i] = guest_byte_from_bitmap(ov->bitmap, (i - 1) * BITS_PER_BYTE); + if (vec[i]) { + DPRINTFN("encoding guest vector byte %3d / %3d: 0x%.2x", + i, vec_len, vec[i]); + } + } + + return fdt_setprop(fdt, fdt_offset, name, vec, vec_len); +} diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 2a1ccf59ea..7cde30ee09 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1392,6 +1392,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) return; } + if (sphb->numa_node != -1 && + (sphb->numa_node >= MAX_NODES || !numa_info[sphb->numa_node].present)) { + error_setg(errp, "Invalid NUMA node ID for PCI host bridge"); + return; + } + sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); namebuf = alloca(strlen(sphb->dtbusname) + 32); @@ -1880,7 +1886,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, } /* Advertise NUMA via ibm,associativity */ - if (nb_numa_nodes > 1) { + if (phb->numa_node != -1) { _FDT(fdt_setprop(fdt, bus_off, "ibm,associativity", associativity, sizeof(associativity))); } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 0db84c816d..bb19944686 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -46,6 +46,7 @@ #include "hw/ppc/spapr_drc.h" #include "qemu/cutils.h" #include "trace.h" +#include "hw/ppc/fdt.h" static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr, uint32_t drc_index) @@ -710,78 +711,60 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) rtas_table[token].fn = fn; } -int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, - hwaddr rtas_size) +void spapr_dt_rtas_tokens(void *fdt, int rtas) { - int ret; int i; - uint32_t lrdr_capacity[5]; - MachineState *machine = MACHINE(qdev_get_machine()); - sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - uint64_t max_hotplug_addr = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); - ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); + for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { + struct rtas_call *call = &rtas_table[i]; + + if (!call->name) { + continue; + } + + _FDT(fdt_setprop_cell(fdt, rtas, call->name, i + RTAS_TOKEN_BASE)); + } +} + +void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr) +{ + int rtas_node; + int ret; + + /* Copy RTAS blob into guest RAM */ + cpu_physical_memory_write(addr, spapr->rtas_blob, spapr->rtas_size); + + ret = fdt_add_mem_rsv(fdt, addr, spapr->rtas_size); if (ret < 0) { error_report("Couldn't add RTAS reserve entry: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", - rtas_addr); + /* Update the device tree with the blob's location */ + rtas_node = fdt_path_offset(fdt, "/rtas"); + assert(rtas_node >= 0); + + ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-base", addr); if (ret < 0) { error_report("Couldn't add linux,rtas-base property: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", - rtas_addr); + ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-entry", addr); if (ret < 0) { error_report("Couldn't add linux,rtas-entry property: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", - rtas_size); + ret = fdt_setprop_cell(fdt, rtas_node, "rtas-size", spapr->rtas_size); if (ret < 0) { error_report("Couldn't add rtas-size property: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - - for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { - struct rtas_call *call = &rtas_table[i]; - - if (!call->name) { - continue; - } - - ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, - i + RTAS_TOKEN_BASE); - if (ret < 0) { - error_report("Couldn't add rtas token for %s: %s", - call->name, fdt_strerror(ret)); - return ret; - } - - } - - lrdr_capacity[0] = cpu_to_be32(max_hotplug_addr >> 32); - lrdr_capacity[1] = cpu_to_be32(max_hotplug_addr & 0xffffffff); - lrdr_capacity[2] = 0; - lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE); - lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads); - ret = qemu_fdt_setprop(fdt, "/rtas", "ibm,lrdr-capacity", lrdr_capacity, - sizeof(lrdr_capacity)); - if (ret < 0) { - error_report("Couldn't add ibm,lrdr-capacity rtas property"); - return ret; - } - - return 0; } static void core_rtas_register_types(void) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 3648aa5960..cc1e09c568 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -36,6 +36,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/ppc/xics.h" +#include "hw/ppc/fdt.h" #include "trace.h" #include <libfdt.h> @@ -624,11 +625,21 @@ static int compare_reg(const void *p1, const void *p2) return 1; } -int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) +void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt) { DeviceState *qdev, **qdevs; BusChild *kid; int i, num, ret = 0; + int node; + + _FDT(node = fdt_add_subnode(fdt, 0, "vdevice")); + + _FDT(fdt_setprop_string(fdt, node, "device_type", "vdevice")); + _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,vdevice")); + _FDT(fdt_setprop_cell(fdt, node, "#address-cells", 1)); + _FDT(fdt_setprop_cell(fdt, node, "#size-cells", 0)); + _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); + _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); /* Count qdevs on the bus list */ num = 0; @@ -650,43 +661,32 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) * to know that will mean they are in forward order in the tree. */ for (i = num - 1; i >= 0; i--) { VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]); + VIOsPAPRDeviceClass *vdc = VIO_SPAPR_DEVICE_GET_CLASS(dev); ret = vio_make_devnode(dev, fdt); - if (ret < 0) { - goto out; + error_report("Couldn't create device node /vdevice/%s@%"PRIx32, + vdc->dt_name, dev->reg); + exit(1); } } - ret = 0; -out: g_free(qdevs); - - return ret; } -int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus) +gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus) { VIOsPAPRDevice *dev; char *name, *path; - int ret, offset; dev = spapr_vty_get_default(bus); - if (!dev) - return 0; - - offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - return offset; + if (!dev) { + return NULL; } name = spapr_vio_get_dev_name(DEVICE(dev)); path = g_strdup_printf("/vdevice/%s", name); - ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path); - g_free(name); - g_free(path); - - return ret; + return path; } diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index b7f8bca1fd..63f6248f1d 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -463,7 +463,6 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { S390PCIBusDevice *pbdev = opaque; - uint32_t io_int_word; uint32_t idx = data >> ZPCI_MSI_VEC_BITS; uint32_t vec = data & ZPCI_MSI_VEC_MASK; uint64_t ind_bit; @@ -489,8 +488,7 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, 0x80 >> ((ind_bit + vec) % 8)); if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8, 0x80 >> (sum_bit % 8))) { - io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI; - s390_io_interrupt(0, 0, 0, io_int_word); + css_adapter_interrupt(pbdev->isc); } } @@ -809,17 +807,11 @@ static uint32_t s390_pci_generate_fid(Error **errp) { uint32_t fid = 0; - while (fid <= ZPCI_MAX_FID) { + do { if (!s390_pci_find_dev_by_fid(fid)) { return fid; } - - if (fid == ZPCI_MAX_FID) { - break; - } - - fid++; - } + } while (fid++ != ZPCI_MAX_FID); error_setg(errp, "no free fid could be found"); return 0; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 80a51049ca..0864d9be12 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -316,6 +316,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) uint64_t offset; uint64_t data; MemoryRegion *mr; + MemTxResult result; uint8_t len; uint32_t fh; uint8_t pcias; @@ -365,8 +366,12 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) return 0; } mr = pbdev->pdev->io_regions[pcias].memory; - memory_region_dispatch_read(mr, offset, &data, len, - MEMTXATTRS_UNSPECIFIED); + result = memory_region_dispatch_read(mr, offset, &data, len, + MEMTXATTRS_UNSPECIFIED); + if (result != MEMTX_OK) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } } else if (pcias == 15) { if ((4 - (offset & 0x3)) < len) { program_interrupt(env, PGM_OPERAND, 4); @@ -444,6 +449,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) uint64_t offset, data; S390PCIBusDevice *pbdev; MemoryRegion *mr; + MemTxResult result; uint8_t len; uint32_t fh; uint8_t pcias; @@ -502,8 +508,12 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) mr = pbdev->pdev->io_regions[pcias].memory; } - memory_region_dispatch_write(mr, offset, data, len, + result = memory_region_dispatch_write(mr, offset, data, len, MEMTXATTRS_UNSPECIFIED); + if (result != MEMTX_OK) { + program_interrupt(env, PGM_OPERAND, 4); + return 0; + } } else if (pcias == 15) { if ((4 - (offset & 0x3)) < len) { program_interrupt(env, PGM_OPERAND, 4); @@ -633,6 +643,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, CPUS390XState *env = &cpu->env; S390PCIBusDevice *pbdev; MemoryRegion *mr; + MemTxResult result; int i; uint32_t fh; uint8_t pcias; @@ -690,7 +701,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, mr = pbdev->pdev->io_regions[pcias].memory; if (!memory_region_access_valid(mr, env->regs[r3], len, true)) { - program_interrupt(env, PGM_ADDRESSING, 6); + program_interrupt(env, PGM_OPERAND, 6); return 0; } @@ -699,9 +710,13 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } for (i = 0; i < len / 8; i++) { - memory_region_dispatch_write(mr, env->regs[r3] + i * 8, + result = memory_region_dispatch_write(mr, env->regs[r3] + i * 8, ldq_p(buffer + i * 8), 8, MEMTXATTRS_UNSPECIFIED); + if (result != MEMTX_OK) { + program_interrupt(env, PGM_OPERAND, 6); + return 0; + } } setcc(cpu, ZPCI_PCI_LS_OK); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 88beaf4bb8..a96319138a 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -341,6 +341,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret) r->req.resid -= r->req.sg->size; r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), r->req.sg, r->sector << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, sdc->dma_readv, r, scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); } else { @@ -539,6 +540,7 @@ static void scsi_write_data(SCSIRequest *req) r->req.resid -= r->req.sg->size; r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), r->req.sg, r->sector << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, sdc->dma_writev, r, scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); } else { diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index b173b94949..9424f0e057 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -189,13 +189,11 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); aio_context_acquire(s->ctx); - virtio_scsi_clear_aio(s); + aio_context_release(s->ctx); blk_drain_all(); /* ensure there are no in-flight requests */ - aio_context_release(s->ctx); - for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index b3915e4fd6..6224288ac3 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -35,10 +35,11 @@ #include "sysemu/sysemu.h" #include "net/net.h" #include "hw/boards.h" -#include "hw/nvram/openbios_firmware_abi.h" #include "hw/scsi/esp.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" +#include "hw/nvram/sun_nvram.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/empty_slot.h" @@ -117,39 +118,17 @@ static void nvram_init(Nvram *nvram, uint8_t *macaddr, int nvram_machine_id, const char *arch) { unsigned int i; - uint32_t start, end; + int sysp_end; uint8_t image[0x1ff0]; - struct OpenBIOS_nvpart_v1 *part_header; NvramClass *k = NVRAM_GET_CLASS(nvram); memset(image, '\0', sizeof(image)); - start = 0; + /* OpenBIOS nvram variables partition */ + sysp_end = chrp_nvram_create_system_partition(image, 0); - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(image, end, prom_envs[i]); - - // End marker - image[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = 0x1fd0; - OpenBIOS_finish_partition(part_header, end - start); + /* Free space partition */ + chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end); Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, nvram_machine_id); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 7b8134ef51..271d8bc592 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -36,7 +36,8 @@ #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/boards.h" -#include "hw/nvram/openbios_firmware_abi.h" +#include "hw/nvram/sun_nvram.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" #include "hw/ide.h" @@ -124,39 +125,17 @@ static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size, const uint8_t *macaddr) { unsigned int i; - uint32_t start, end; + int sysp_end; uint8_t image[0x1ff0]; - struct OpenBIOS_nvpart_v1 *part_header; NvramClass *k = NVRAM_GET_CLASS(nvram); memset(image, '\0', sizeof(image)); - start = 0; + /* OpenBIOS nvram variables partition */ + sysp_end = chrp_nvram_create_system_partition(image, 0); - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(image, end, prom_envs[i]); - - // End marker - image[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = 0x1fd0; - OpenBIOS_finish_partition(part_header, end - start); + /* Free space partition */ + chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end); Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80); diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 712d1aece5..4ed96e970a 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -26,7 +26,6 @@ #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/ptimer.h" -#include "qemu/timer.h" #include "qemu/main-loop.h" #include "trace.h" diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index e88c0d20bc..9234eb3459 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -165,8 +165,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); if (ret != in_len) { - if (!tpm_pt->tpm_op_canceled || - (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { + if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { error_report("tpm_passthrough: error while transmitting data " "to TPM: %s (%i)", strerror(errno), errno); @@ -178,8 +177,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); if (ret < 0) { - if (!tpm_pt->tpm_op_canceled || - (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { + if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { error_report("tpm_passthrough: error while reading data from " "TPM: %s (%i)", strerror(errno), errno); diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 381e7266ea..a6440fef91 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -34,7 +34,6 @@ #include "qapi/error.h" #include "qemu-common.h" #include "qemu/main-loop.h" -#include "sysemu/tpm_backend.h" #define DEBUG_TIS 0 diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index 31cd171016..032078fd3e 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -13,7 +13,6 @@ #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" -#include "qemu-common.h" #include "ui/console.h" #include "elf.h" #include "exec/address-spaces.h" diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 3213f9f8af..eceb5f3ee2 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -547,7 +547,7 @@ static int emulated_initfn(CCIDCardState *base) return 0; } -static int emulated_exitfn(CCIDCardState *base) +static void emulated_exitfn(CCIDCardState *base) { EmulatedState *card = EMULATED_CCID_CARD(base); VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); @@ -564,7 +564,6 @@ static int emulated_exitfn(CCIDCardState *base) qemu_mutex_destroy(&card->handle_apdu_mutex); qemu_mutex_destroy(&card->vreader_mutex); qemu_mutex_destroy(&card->event_list_mutex); - return 0; } static Property emulated_card_properties[] = { diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 325129a2f6..88cb6d8978 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -365,11 +365,6 @@ static int passthru_initfn(CCIDCardState *base) return 0; } -static int passthru_exitfn(CCIDCardState *base) -{ - return 0; -} - static VMStateDescription passthru_vmstate = { .name = "ccid-card-passthru", .version_id = 1, @@ -396,7 +391,6 @@ static void passthru_class_initfn(ObjectClass *klass, void *data) CCIDCardClass *cc = CCID_CARD_CLASS(klass); cc->initfn = passthru_initfn; - cc->exitfn = passthru_exitfn; cc->get_atr = passthru_get_atr; cc->apdu_from_guest = passthru_apdu_from_guest; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); diff --git a/hw/usb/ccid.h b/hw/usb/ccid.h index 9334da8acd..1f070116d6 100644 --- a/hw/usb/ccid.h +++ b/hw/usb/ccid.h @@ -33,7 +33,7 @@ typedef struct CCIDCardClass { void (*apdu_from_guest)(CCIDCardState *card, const uint8_t *apdu, uint32_t len); - int (*exitfn)(CCIDCardState *card); + void (*exitfn)(CCIDCardState *card); int (*initfn)(CCIDCardState *card); } CCIDCardClass; diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 58d95fffb2..9cb0f50750 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -17,7 +17,6 @@ #include <sys/statvfs.h> #ifdef CONFIG_INOTIFY1 #include <sys/inotify.h> -#include "qapi/error.h" #include "qemu/main-loop.h" #endif diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index af4b851356..89e11b68c4 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -508,14 +508,14 @@ static void ccid_card_apdu_from_guest(CCIDCardState *card, } } -static int ccid_card_exitfn(CCIDCardState *card) +static void ccid_card_exitfn(CCIDCardState *card) { CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); if (cc->exitfn) { - return cc->exitfn(card); + cc->exitfn(card); } - return 0; + } static int ccid_card_initfn(CCIDCardState *card) @@ -1279,7 +1279,6 @@ void ccid_card_card_inserted(CCIDCardState *card) static int ccid_card_exit(DeviceState *qdev) { - int ret = 0; CCIDCardState *card = CCID_CARD(qdev); USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); USBCCIDState *s = USB_CCID_DEV(dev); @@ -1287,9 +1286,9 @@ static int ccid_card_exit(DeviceState *qdev) if (ccid_card_inserted(s)) { ccid_card_card_removed(card); } - ret = ccid_card_exitfn(card); + ccid_card_exitfn(card); s->card = NULL; - return ret; + return 0; } static int ccid_card_init(DeviceState *qdev) diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index de2ebd6210..1b3c2fb3c7 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -47,7 +47,7 @@ struct timeval tv; \ \ gettimeofday(&tv, NULL); \ - xen_be_printf(xendev, lvl, "%8ld.%06ld xen-usb(%s):" fmt, \ + xen_pv_printf(xendev, lvl, "%8ld.%06ld xen-usb(%s):" fmt, \ tv.tv_sec, tv.tv_usec, __func__, ##args); \ } #define TR_BUS(xendev, fmt, args...) TR(xendev, 2, fmt, ##args) @@ -153,7 +153,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) } if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) { - xen_be_printf(xendev, 0, "bad number of segments in request (%d)\n", + xen_pv_printf(xendev, 0, "bad number of segments in request (%d)\n", nr_segs); return -EINVAL; } @@ -161,7 +161,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < nr_segs; i++) { if ((unsigned)usbback_req->req.seg[i].offset + (unsigned)usbback_req->req.seg[i].length > XC_PAGE_SIZE) { - xen_be_printf(xendev, 0, "segment crosses page boundary\n"); + xen_pv_printf(xendev, 0, "segment crosses page boundary\n"); return -EINVAL; } } @@ -199,7 +199,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) */ if (!usbback_req->nr_extra_segs) { - xen_be_printf(xendev, 0, "iso request without descriptor segments\n"); + xen_pv_printf(xendev, 0, "iso request without descriptor segments\n"); return -EINVAL; } @@ -314,7 +314,7 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify); if (notify) { - xen_be_send_notify(xendev); + xen_pv_send_notify(xendev); } } @@ -551,14 +551,14 @@ static void usbback_dispatch(struct usbback_req *usbback_req) ret = usbback_init_packet(usbback_req); if (ret) { - xen_be_printf(&usbif->xendev, 0, "invalid request\n"); + xen_pv_printf(&usbif->xendev, 0, "invalid request\n"); ret = -ESHUTDOWN; goto fail_free_urb; } ret = usbback_gnttab_map(usbback_req); if (ret) { - xen_be_printf(&usbif->xendev, 0, "invalid buffer, ret=%d\n", ret); + xen_pv_printf(&usbif->xendev, 0, "invalid buffer, ret=%d\n", ret); ret = -ESHUTDOWN; goto fail_free_urb; } @@ -590,7 +590,7 @@ static void usbback_hotplug_notify(struct usbback_info *usbif) /* Check for full ring. */ if ((RING_SIZE(ring) - ring->rsp_prod_pvt - ring->req_cons) == 0) { - xen_be_send_notify(&usbif->xendev); + xen_pv_send_notify(&usbif->xendev); return; } @@ -609,7 +609,7 @@ static void usbback_hotplug_notify(struct usbback_info *usbif) RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify); if (notify) { - xen_be_send_notify(&usbif->xendev); + xen_pv_send_notify(&usbif->xendev); } TR_BUS(&usbif->xendev, "hotplug port %d speed %d\n", usb_hp->port, @@ -646,7 +646,7 @@ static void usbback_bh(void *opaque) if (RING_REQUEST_PROD_OVERFLOW(urb_ring, rp)) { rc = urb_ring->rsp_prod_pvt; - xen_be_printf(&usbif->xendev, 0, "domU provided bogus ring requests " + xen_pv_printf(&usbif->xendev, 0, "domU provided bogus ring requests " "(%#x - %#x = %u). Halting ring processing.\n", rp, rc, rp - rc); usbif->ring_error = true; @@ -744,7 +744,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, portname = strchr(busid, '-'); if (!portname) { - xen_be_printf(&usbif->xendev, 0, "device %s illegal specification\n", + xen_pv_printf(&usbif->xendev, 0, "device %s illegal specification\n", busid); return; } @@ -783,7 +783,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, break; } if (speed == USBIF_SPEED_NONE) { - xen_be_printf(&usbif->xendev, 0, "device %s wrong speed\n", busid); + xen_pv_printf(&usbif->xendev, 0, "device %s wrong speed\n", busid); object_unparent(OBJECT(usbif->ports[port - 1].dev)); usbif->ports[port - 1].dev = NULL; return; @@ -800,7 +800,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, err: QDECREF(qdict); snprintf(p->path, sizeof(p->path), "%d", 99); - xen_be_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); + xen_pv_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); } static void usbback_process_port(struct usbback_info *usbif, unsigned port) @@ -811,7 +811,7 @@ static void usbback_process_port(struct usbback_info *usbif, unsigned port) snprintf(node, sizeof(node), "port/%d", port); busid = xenstore_read_be_str(&usbif->xendev, node); if (busid == NULL) { - xen_be_printf(&usbif->xendev, 0, "xenstore_read %s failed\n", node); + xen_pv_printf(&usbif->xendev, 0, "xenstore_read %s failed\n", node); return; } @@ -834,7 +834,7 @@ static void usbback_disconnect(struct XenDevice *xendev) usbif = container_of(xendev, struct usbback_info, xendev); - xen_be_unbind_evtchn(xendev); + xen_pv_unbind_evtchn(xendev); if (usbif->urb_sring) { xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1); @@ -868,15 +868,15 @@ static int usbback_connect(struct XenDevice *xendev) usbif = container_of(xendev, struct usbback_info, xendev); if (xenstore_read_fe_int(xendev, "urb-ring-ref", &urb_ring_ref)) { - xen_be_printf(xendev, 0, "error reading urb-ring-ref\n"); + xen_pv_printf(xendev, 0, "error reading urb-ring-ref\n"); return -1; } if (xenstore_read_fe_int(xendev, "conn-ring-ref", &conn_ring_ref)) { - xen_be_printf(xendev, 0, "error reading conn-ring-ref\n"); + xen_pv_printf(xendev, 0, "error reading conn-ring-ref\n"); return -1; } if (xenstore_read_fe_int(xendev, "event-channel", &xendev->remote_port)) { - xen_be_printf(xendev, 0, "error reading event-channel\n"); + xen_pv_printf(xendev, 0, "error reading event-channel\n"); return -1; } @@ -887,7 +887,7 @@ static int usbback_connect(struct XenDevice *xendev) conn_ring_ref, PROT_READ | PROT_WRITE); if (!usbif->urb_sring || !usbif->conn_sring) { - xen_be_printf(xendev, 0, "error mapping rings\n"); + xen_pv_printf(xendev, 0, "error mapping rings\n"); usbback_disconnect(xendev); return -1; } @@ -899,7 +899,7 @@ static int usbback_connect(struct XenDevice *xendev) xen_be_bind_evtchn(xendev); - xen_be_printf(xendev, 1, "urb-ring-ref %d, conn-ring-ref %d, " + xen_pv_printf(xendev, 1, "urb-ring-ref %d, conn-ring-ref %d, " "remote port %d, local port %d\n", urb_ring_ref, conn_ring_ref, xendev->remote_port, xendev->local_port); @@ -935,12 +935,12 @@ static int usbback_init(struct XenDevice *xendev) if (xenstore_read_be_int(xendev, "num-ports", &usbif->num_ports) || usbif->num_ports < 1 || usbif->num_ports > USBBACK_MAXPORTS) { - xen_be_printf(xendev, 0, "num-ports not readable or out of bounds\n"); + xen_pv_printf(xendev, 0, "num-ports not readable or out of bounds\n"); return -1; } if (xenstore_read_be_int(xendev, "usb-ver", &usbif->usb_ver) || (usbif->usb_ver != USB_VER_USB11 && usbif->usb_ver != USB_VER_USB20)) { - xen_be_printf(xendev, 0, "usb-ver not readable or out of bounds\n"); + xen_pv_printf(xendev, 0, "usb-ver not readable or out of bounds\n"); return -1; } @@ -1028,7 +1028,7 @@ static void usbback_alloc(struct XenDevice *xendev) /* max_grants: for each request and for the rings (request and connect). */ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) { - xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", strerror(errno)); } } diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index d3670940b7..591cdc229d 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -1,5 +1,5 @@ # xen backend driver support -common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o +common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o xen_pvdev.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 69a238817e..41ba5c585a 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -30,6 +30,7 @@ #include "sysemu/char.h" #include "qemu/log.h" #include "hw/xen/xen_backend.h" +#include "hw/xen/xen_pvdev.h" #include <xen/grant_table.h> @@ -46,129 +47,7 @@ struct xs_handle *xenstore = NULL; const char *xen_protocol; /* private */ -struct xs_dirs { - char *xs_dir; - QTAILQ_ENTRY(xs_dirs) list; -}; -static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = - QTAILQ_HEAD_INITIALIZER(xs_cleanup); - -static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs); -static int debug = 0; - -/* ------------------------------------------------------------- */ - -static void xenstore_cleanup_dir(char *dir) -{ - struct xs_dirs *d; - - d = g_malloc(sizeof(*d)); - d->xs_dir = dir; - QTAILQ_INSERT_TAIL(&xs_cleanup, d, list); -} - -void xen_config_cleanup(void) -{ - struct xs_dirs *d; - - QTAILQ_FOREACH(d, &xs_cleanup, list) { - xs_rm(xenstore, 0, d->xs_dir); - } -} - -int xenstore_write_str(const char *base, const char *node, const char *val) -{ - char abspath[XEN_BUFSIZE]; - - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - if (!xs_write(xenstore, 0, abspath, val, strlen(val))) { - return -1; - } - return 0; -} - -char *xenstore_read_str(const char *base, const char *node) -{ - char abspath[XEN_BUFSIZE]; - unsigned int len; - char *str, *ret = NULL; - - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - str = xs_read(xenstore, 0, abspath, &len); - if (str != NULL) { - /* move to qemu-allocated memory to make sure - * callers can savely g_free() stuff. */ - ret = g_strdup(str); - free(str); - } - return ret; -} - -int xenstore_mkdir(char *path, int p) -{ - struct xs_permissions perms[2] = { - { - .id = 0, /* set owner: dom0 */ - }, { - .id = xen_domid, - .perms = p, - } - }; - - if (!xs_mkdir(xenstore, 0, path)) { - xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", path); - return -1; - } - xenstore_cleanup_dir(g_strdup(path)); - - if (!xs_set_permissions(xenstore, 0, path, perms, 2)) { - xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", path); - return -1; - } - return 0; -} - -int xenstore_write_int(const char *base, const char *node, int ival) -{ - char val[12]; - - snprintf(val, sizeof(val), "%d", ival); - return xenstore_write_str(base, node, val); -} - -int xenstore_write_int64(const char *base, const char *node, int64_t ival) -{ - char val[21]; - - snprintf(val, sizeof(val), "%"PRId64, ival); - return xenstore_write_str(base, node, val); -} - -int xenstore_read_int(const char *base, const char *node, int *ival) -{ - char *val; - int rc = -1; - - val = xenstore_read_str(base, node); - if (val && 1 == sscanf(val, "%d", ival)) { - rc = 0; - } - g_free(val); - return rc; -} - -int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval) -{ - char *val; - int rc = -1; - - val = xenstore_read_str(base, node); - if (val && 1 == sscanf(val, "%"SCNu64, uval)) { - rc = 0; - } - g_free(val); - return rc; -} +static int debug; int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) { @@ -205,27 +84,14 @@ int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) return xenstore_read_int(xendev->fe, node, ival); } -int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, uint64_t *uval) +int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, + uint64_t *uval) { return xenstore_read_uint64(xendev->fe, node, uval); } /* ------------------------------------------------------------- */ -const char *xenbus_strstate(enum xenbus_state state) -{ - static const char *const name[] = { - [ XenbusStateUnknown ] = "Unknown", - [ XenbusStateInitialising ] = "Initialising", - [ XenbusStateInitWait ] = "InitWait", - [ XenbusStateInitialised ] = "Initialised", - [ XenbusStateConnected ] = "Connected", - [ XenbusStateClosing ] = "Closing", - [ XenbusStateClosed ] = "Closed", - }; - return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; -} - int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) { int rc; @@ -234,33 +100,12 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) if (rc < 0) { return rc; } - xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xen_pv_printf(xendev, 1, "backend state: %s -> %s\n", xenbus_strstate(xendev->be_state), xenbus_strstate(state)); xendev->be_state = state; return 0; } -/* ------------------------------------------------------------- */ - -struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev) -{ - struct XenDevice *xendev; - - QTAILQ_FOREACH(xendev, &xendevs, next) { - if (xendev->dom != dom) { - continue; - } - if (xendev->dev != dev) { - continue; - } - if (strcmp(xendev->type, type) != 0) { - continue; - } - return xendev; - } - return NULL; -} - /* * get xen backend device, allocate a new one if it doesn't exist. */ @@ -269,7 +114,7 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, { struct XenDevice *xendev; - xendev = xen_be_find_xendev(type, dom, dev); + xendev = xen_pv_find_xendev(type, dom, dev); if (xendev) { return xendev; } @@ -291,7 +136,7 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, xendev->evtchndev = xenevtchn_open(NULL, 0); if (xendev->evtchndev == NULL) { - xen_be_printf(NULL, 0, "can't open evtchn device\n"); + xen_pv_printf(NULL, 0, "can't open evtchn device\n"); g_free(xendev); return NULL; } @@ -300,7 +145,7 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { xendev->gnttabdev = xengnttab_open(NULL, 0); if (xendev->gnttabdev == NULL) { - xen_be_printf(NULL, 0, "can't open gnttab device\n"); + xen_pv_printf(NULL, 0, "can't open gnttab device\n"); xenevtchn_close(xendev->evtchndev); g_free(xendev); return NULL; @@ -309,7 +154,7 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, xendev->gnttabdev = NULL; } - QTAILQ_INSERT_TAIL(&xendevs, xendev, next); + xen_pv_insert_xendev(xendev); if (xendev->ops->alloc) { xendev->ops->alloc(xendev); @@ -318,32 +163,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, return xendev; } -/* - * release xen backend device. - */ -static void xen_be_del_xendev(struct XenDevice *xendev) -{ - if (xendev->ops->free) { - xendev->ops->free(xendev); - } - - if (xendev->fe) { - char token[XEN_BUFSIZE]; - snprintf(token, sizeof(token), "fe:%p", xendev); - xs_unwatch(xenstore, xendev->fe, token); - g_free(xendev->fe); - } - - if (xendev->evtchndev != NULL) { - xenevtchn_close(xendev->evtchndev); - } - if (xendev->gnttabdev != NULL) { - xengnttab_close(xendev->gnttabdev); - } - - QTAILQ_REMOVE(&xendevs, xendev, next); - g_free(xendev); -} /* * Sync internal data structures on xenstore updates. @@ -359,7 +178,7 @@ static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) } if (node) { - xen_be_printf(xendev, 2, "backend update: %s\n", node); + xen_pv_printf(xendev, 2, "backend update: %s\n", node); if (xendev->ops->backend_changed) { xendev->ops->backend_changed(xendev, node); } @@ -375,7 +194,7 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) fe_state = XenbusStateUnknown; } if (xendev->fe_state != fe_state) { - xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xen_pv_printf(xendev, 1, "frontend state: %s -> %s\n", xenbus_strstate(xendev->fe_state), xenbus_strstate(fe_state)); } @@ -385,12 +204,13 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) g_free(xendev->protocol); xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); if (xendev->protocol) { - xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + xen_pv_printf(xendev, 1, "frontend protocol: %s\n", + xendev->protocol); } } if (node) { - xen_be_printf(xendev, 2, "frontend update: %s\n", node); + xen_pv_printf(xendev, 2, "frontend update: %s\n", node); if (xendev->ops->frontend_changed) { xendev->ops->frontend_changed(xendev, node); } @@ -414,26 +234,26 @@ static int xen_be_try_setup(struct XenDevice *xendev) int be_state; if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { - xen_be_printf(xendev, 0, "reading backend state failed\n"); + xen_pv_printf(xendev, 0, "reading backend state failed\n"); return -1; } if (be_state != XenbusStateInitialising) { - xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xen_pv_printf(xendev, 0, "initial backend state is wrong (%s)\n", xenbus_strstate(be_state)); return -1; } xendev->fe = xenstore_read_be_str(xendev, "frontend"); if (xendev->fe == NULL) { - xen_be_printf(xendev, 0, "reading frontend path failed\n"); + xen_pv_printf(xendev, 0, "reading frontend path failed\n"); return -1; } /* setup frontend watch */ snprintf(token, sizeof(token), "fe:%p", xendev); if (!xs_watch(xenstore, xendev->fe, token)) { - xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xen_pv_printf(xendev, 0, "watching frontend path (%s) failed\n", xendev->fe); return -1; } @@ -457,7 +277,7 @@ static int xen_be_try_init(struct XenDevice *xendev) int rc = 0; if (!xendev->online) { - xen_be_printf(xendev, 1, "not online\n"); + xen_pv_printf(xendev, 1, "not online\n"); return -1; } @@ -465,7 +285,7 @@ static int xen_be_try_init(struct XenDevice *xendev) rc = xendev->ops->init(xendev); } if (rc != 0) { - xen_be_printf(xendev, 1, "init() failed\n"); + xen_pv_printf(xendev, 1, "init() failed\n"); return rc; } @@ -488,9 +308,9 @@ static int xen_be_try_initialise(struct XenDevice *xendev) if (xendev->fe_state != XenbusStateInitialised && xendev->fe_state != XenbusStateConnected) { if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { - xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + xen_pv_printf(xendev, 2, "frontend not ready, ignoring\n"); } else { - xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + xen_pv_printf(xendev, 2, "frontend not ready (yet)\n"); return -1; } } @@ -499,7 +319,7 @@ static int xen_be_try_initialise(struct XenDevice *xendev) rc = xendev->ops->initialise(xendev); } if (rc != 0) { - xen_be_printf(xendev, 0, "initialise() failed\n"); + xen_pv_printf(xendev, 0, "initialise() failed\n"); return rc; } @@ -520,9 +340,9 @@ static void xen_be_try_connected(struct XenDevice *xendev) if (xendev->fe_state != XenbusStateConnected) { if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { - xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + xen_pv_printf(xendev, 2, "frontend not ready, ignoring\n"); } else { - xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + xen_pv_printf(xendev, 2, "frontend not ready (yet)\n"); return; } } @@ -556,7 +376,7 @@ static int xen_be_try_reset(struct XenDevice *xendev) return -1; } - xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); + xen_pv_printf(xendev, 1, "device reset (for re-connect)\n"); xen_be_set_state(xendev, XenbusStateInitialising); return 0; } @@ -617,7 +437,8 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); snprintf(path, sizeof(path), "backend/%s/%d", type, dom); if (!xs_watch(xenstore, path, token)) { - xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); + xen_pv_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", + path); return -1; } @@ -637,8 +458,8 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) return 0; } -static void xenstore_update_be(char *watch, char *type, int dom, - struct XenDevOps *ops) +void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) { struct XenDevice *xendev; char path[XEN_BUFSIZE], *bepath; @@ -662,7 +483,7 @@ static void xenstore_update_be(char *watch, char *type, int dom, if (xendev != NULL) { bepath = xs_read(xenstore, 0, xendev->be, &len); if (bepath == NULL) { - xen_be_del_xendev(xendev); + xen_pv_del_xendev(xendev); } else { free(bepath); xen_be_backend_changed(xendev, path); @@ -671,7 +492,7 @@ static void xenstore_update_be(char *watch, char *type, int dom, } } -static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +void xenstore_update_fe(char *watch, struct XenDevice *xendev) { char *node; unsigned int len; @@ -688,56 +509,13 @@ static void xenstore_update_fe(char *watch, struct XenDevice *xendev) xen_be_frontend_changed(xendev, node); xen_be_check_state(xendev); } - -static void xenstore_update(void *unused) -{ - char **vec = NULL; - intptr_t type, ops, ptr; - unsigned int dom, count; - - vec = xs_read_watch(xenstore, &count); - if (vec == NULL) { - goto cleanup; - } - - if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, - &type, &dom, &ops) == 3) { - xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); - } - if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { - xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); - } - -cleanup: - free(vec); -} - -static void xen_be_evtchn_event(void *opaque) -{ - struct XenDevice *xendev = opaque; - evtchn_port_t port; - - port = xenevtchn_pending(xendev->evtchndev); - if (port != xendev->local_port) { - xen_be_printf(xendev, 0, - "xenevtchn_pending returned %d (expected %d)\n", - port, xendev->local_port); - return; - } - xenevtchn_unmask(xendev->evtchndev, port); - - if (xendev->ops->event) { - xendev->ops->event(xendev); - } -} - /* -------------------------------------------------------------------- */ int xen_be_init(void) { xenstore = xs_daemon_open(); if (!xenstore) { - xen_be_printf(NULL, 0, "can't connect to xenstored\n"); + xen_pv_printf(NULL, 0, "can't connect to xenstored\n"); return -1; } @@ -798,69 +576,15 @@ int xen_be_bind_evtchn(struct XenDevice *xendev) xendev->local_port = xenevtchn_bind_interdomain (xendev->evtchndev, xendev->dom, xendev->remote_port); if (xendev->local_port == -1) { - xen_be_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n"); + xen_pv_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n"); return -1; } - xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), - xen_be_evtchn_event, NULL, xendev); + xen_pv_evtchn_event, NULL, xendev); return 0; } -void xen_be_unbind_evtchn(struct XenDevice *xendev) -{ - if (xendev->local_port == -1) { - return; - } - qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL); - xenevtchn_unbind(xendev->evtchndev, xendev->local_port); - xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); - xendev->local_port = -1; -} - -int xen_be_send_notify(struct XenDevice *xendev) -{ - return xenevtchn_notify(xendev->evtchndev, xendev->local_port); -} - -/* - * msg_level: - * 0 == errors (stderr + logfile). - * 1 == informative debug messages (logfile only). - * 2 == noisy debug messages (logfile only). - * 3 == will flood your log (logfile only). - */ -void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) -{ - va_list args; - - if (xendev) { - if (msg_level > xendev->debug) { - return; - } - qemu_log("xen be: %s: ", xendev->name); - if (msg_level == 0) { - fprintf(stderr, "xen be: %s: ", xendev->name); - } - } else { - if (msg_level > debug) { - return; - } - qemu_log("xen be core: "); - if (msg_level == 0) { - fprintf(stderr, "xen be core: "); - } - } - va_start(args, fmt); - qemu_log_vprintf(fmt, args); - va_end(args); - if (msg_level == 0) { - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - } - qemu_log_flush(); -} static int xen_sysdev_init(SysBusDevice *dev) { diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index b7d290df6c..a80e78c0dc 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -55,7 +55,7 @@ int xen_config_dev_blk(DriveInfo *disk) const char *filename = qemu_opt_get(disk->opts, "file"); snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit); - xen_be_printf(NULL, 1, "config disk %d [%s]: %s\n", + xen_pv_printf(NULL, 1, "config disk %d [%s]: %s\n", disk->unit, device_name, filename); xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); @@ -83,7 +83,7 @@ int xen_config_dev_nic(NICInfo *nic) snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2], nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]); - xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", vlan_id, mac); + xen_pv_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", vlan_id, mac); xen_config_dev_dirs("vif", "qnic", vlan_id, fe, be, sizeof(fe)); /* frontend */ diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c new file mode 100644 index 0000000000..405e15484c --- /dev/null +++ b/hw/xen/xen_pvdev.c @@ -0,0 +1,316 @@ +/* + * Xen para-virtualization device + * + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#include "qemu/osdep.h" + +#include "hw/xen/xen_backend.h" +#include "hw/xen/xen_pvdev.h" + +/* private */ +static int debug; + +struct xs_dirs { + char *xs_dir; + QTAILQ_ENTRY(xs_dirs) list; +}; + +static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = + QTAILQ_HEAD_INITIALIZER(xs_cleanup); + +static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = + QTAILQ_HEAD_INITIALIZER(xendevs); + +/* ------------------------------------------------------------- */ + +static void xenstore_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = g_malloc(sizeof(*d)); + d->xs_dir = dir; + QTAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + QTAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + +int xenstore_mkdir(char *path, int p) +{ + struct xs_permissions perms[2] = { + { + .id = 0, /* set owner: dom0 */ + }, { + .id = xen_domid, + .perms = p, + } + }; + + if (!xs_mkdir(xenstore, 0, path)) { + xen_pv_printf(NULL, 0, "xs_mkdir %s: failed\n", path); + return -1; + } + xenstore_cleanup_dir(g_strdup(path)); + + if (!xs_set_permissions(xenstore, 0, path, perms, 2)) { + xen_pv_printf(NULL, 0, "xs_set_permissions %s: failed\n", path); + return -1; + } + return 0; +} + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[XEN_BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) { + return -1; + } + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[XEN_BUFSIZE]; + unsigned int len; + char *str, *ret = NULL; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + str = xs_read(xenstore, 0, abspath, &len); + if (str != NULL) { + /* move to qemu-allocated memory to make sure + * callers can savely g_free() stuff. */ + ret = g_strdup(str); + free(str); + } + return ret; +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[12]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_write_int64(const char *base, const char *node, int64_t ival) +{ + char val[21]; + + snprintf(val, sizeof(val), "%"PRId64, ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) { + rc = 0; + } + g_free(val); + return rc; +} + +int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%"SCNu64, uval)) { + rc = 0; + } + g_free(val); + return rc; +} + +void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (vec == NULL) { + goto cleanup; + } + + if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops) == 3) { + xenstore_update_be(vec[XS_WATCH_PATH], (void *)type, dom, (void*)ops); + } + if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { + xenstore_update_fe(vec[XS_WATCH_PATH], (void *)ptr); + } + +cleanup: + free(vec); +} + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [XenbusStateUnknown] = "Unknown", + [XenbusStateInitialising] = "Initialising", + [XenbusStateInitWait] = "InitWait", + [XenbusStateInitialised] = "Initialised", + [XenbusStateConnected] = "Connected", + [XenbusStateClosing] = "Closing", + [XenbusStateClosed] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +/* + * msg_level: + * 0 == errors (stderr + logfile). + * 1 == informative debug messages (logfile only). + * 2 == noisy debug messages (logfile only). + * 3 == will flood your log (logfile only). + */ +void xen_pv_printf(struct XenDevice *xendev, int msg_level, + const char *fmt, ...) +{ + va_list args; + + if (xendev) { + if (msg_level > xendev->debug) { + return; + } + qemu_log("xen be: %s: ", xendev->name); + if (msg_level == 0) { + fprintf(stderr, "xen be: %s: ", xendev->name); + } + } else { + if (msg_level > debug) { + return; + } + qemu_log("xen be core: "); + if (msg_level == 0) { + fprintf(stderr, "xen be core: "); + } + } + va_start(args, fmt); + qemu_log_vprintf(fmt, args); + va_end(args); + if (msg_level == 0) { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + } + qemu_log_flush(); +} + +void xen_pv_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xenevtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_pv_printf(xendev, 0, + "xenevtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xenevtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) { + xendev->ops->event(xendev); + } +} + +void xen_pv_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) { + return; + } + qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xenevtchn_unbind(xendev->evtchndev, xendev->local_port); + xen_pv_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); + xendev->local_port = -1; +} + +int xen_pv_send_notify(struct XenDevice *xendev) +{ + return xenevtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + QTAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) { + continue; + } + if (xendev->dev != dev) { + continue; + } + if (strcmp(xendev->type, type) != 0) { + continue; + } + return xendev; + } + return NULL; +} + +/* + * release xen backend device. + */ +void xen_pv_del_xendev(struct XenDevice *xendev) +{ + if (xendev->ops->free) { + xendev->ops->free(xendev); + } + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + g_free(xendev->fe); + } + + if (xendev->evtchndev != NULL) { + xenevtchn_close(xendev->evtchndev); + } + if (xendev->gnttabdev != NULL) { + xengnttab_close(xendev->gnttabdev); + } + + QTAILQ_REMOVE(&xendevs, xendev, next); + g_free(xendev); +} + +void xen_pv_insert_xendev(struct XenDevice *xendev) +{ + QTAILQ_INSERT_TAIL(&xendevs, xendev, next); +} diff --git a/include/block/aio.h b/include/block/aio.h index b9fe2cb37e..c7ae27c91c 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -18,7 +18,6 @@ #include "qemu/queue.h" #include "qemu/event_notifier.h" #include "qemu/thread.h" -#include "qemu/rfifolock.h" #include "qemu/timer.h" typedef struct BlockAIOCB BlockAIOCB; @@ -54,7 +53,7 @@ struct AioContext { GSource source; /* Protects all fields from multi-threaded access */ - RFifoLock lock; + QemuRecMutex lock; /* The list of registered AIO handlers */ QLIST_HEAD(, AioHandler) aio_handlers; @@ -116,9 +115,6 @@ struct AioContext { bool notified; EventNotifier notifier; - /* Scheduling this BH forces the event loop it iterate */ - QEMUBH *notify_dummy_bh; - /* Thread pool for performing work and receiving completion callbacks */ struct ThreadPool *thread_pool; @@ -453,6 +449,24 @@ static inline bool aio_node_check(AioContext *ctx, bool is_external) } /** + * Return the AioContext whose event loop runs in the current thread. + * + * If called from an IOThread this will be the IOThread's AioContext. If + * called from another thread it will be the main loop AioContext. + */ +AioContext *qemu_get_current_aio_context(void); + +/** + * @ctx: the aio context + * + * Return whether we are running in the I/O thread that manages @ctx. + */ +static inline bool aio_context_in_iothread(AioContext *ctx) +{ + return ctx == qemu_get_current_aio_context(); +} + +/** * aio_context_setup: * @ctx: the aio context * diff --git a/include/block/block.h b/include/block/block.h index 107c603605..b7dc7d54ae 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -218,7 +218,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, int flags); -int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); +int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp); int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp); int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); @@ -314,17 +314,11 @@ BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs, - int64_t offset, int count, - BlockCompletionFunc *cb, void *opaque); void bdrv_aio_cancel(BlockAIOCB *acb); void bdrv_aio_cancel_async(BlockAIOCB *acb); /* sg packet commands */ -int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf); -BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque); +int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); /* Invalidate any cached metadata used by image formats */ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); @@ -340,6 +334,35 @@ void bdrv_drain(BlockDriverState *bs); void coroutine_fn bdrv_co_drain(BlockDriverState *bs); void bdrv_drain_all(void); +#define BDRV_POLL_WHILE(bs, cond) ({ \ + bool waited_ = false; \ + BlockDriverState *bs_ = (bs); \ + AioContext *ctx_ = bdrv_get_aio_context(bs_); \ + if (aio_context_in_iothread(ctx_)) { \ + while ((cond)) { \ + aio_poll(ctx_, true); \ + waited_ = true; \ + } \ + } else { \ + assert(qemu_get_current_aio_context() == \ + qemu_get_aio_context()); \ + /* Ask bdrv_dec_in_flight to wake up the main \ + * QEMU AioContext. Extra I/O threads never take \ + * other I/O threads' AioContexts (see for example \ + * block_job_defer_to_main_loop for how to do it). \ + */ \ + assert(!bs_->wakeup); \ + bs_->wakeup = true; \ + while ((cond)) { \ + aio_context_release(ctx_); \ + aio_poll(qemu_get_aio_context(), true); \ + aio_context_acquire(ctx_); \ + waited_ = true; \ + } \ + bs_->wakeup = false; \ + } \ + waited_; }) + int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count); int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int count); int bdrv_has_zero_init_1(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index 3e79228eb0..e7ff58419c 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -62,8 +62,6 @@ enum BdrvTrackedRequestType { BDRV_TRACKED_READ, BDRV_TRACKED_WRITE, - BDRV_TRACKED_FLUSH, - BDRV_TRACKED_IOCTL, BDRV_TRACKED_DISCARD, }; @@ -244,6 +242,8 @@ struct BlockDriver { BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); + int coroutine_fn (*bdrv_co_ioctl)(BlockDriverState *bs, + unsigned long int req, void *buf); /* List of options for creating images, terminated by name == NULL */ QemuOptsList *create_opts; @@ -443,7 +443,7 @@ struct BlockDriverState { note this is a reference count */ CoQueue flush_queue; /* Serializing flush queue */ - BdrvTrackedRequest *active_flush_req; /* Flush request in flight */ + bool active_flush_req; /* Flush request in flight? */ unsigned int write_gen; /* Current data generation */ unsigned int flushed_gen; /* Flushed write generation */ @@ -471,9 +471,12 @@ struct BlockDriverState { /* Callback before write request is processed */ NotifierWithReturnList before_write_notifiers; - /* number of in-flight serialising requests */ + /* number of in-flight requests; overall and serialising */ + unsigned int in_flight; unsigned int serialising_in_flight; + bool wakeup; + /* Offset after the highest byte written to */ uint64_t wr_highest_offset; @@ -632,6 +635,21 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, void (*aio_context_detached)(void *), void *opaque); +/** + * bdrv_wakeup: + * @bs: The BlockDriverState for which an I/O operation has been completed. + * + * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During + * synchronous I/O on a BlockDriverState that is attached to another + * I/O thread, the main thread lets the I/O thread's event loop run, + * waiting for the I/O operation to complete. A bdrv_wakeup will wake + * up the main thread if necessary. + * + * Manual calls to bdrv_wakeup are rarely necessary, because + * bdrv_dec_in_flight already calls it. + */ +void bdrv_wakeup(BlockDriverState *bs); + #ifdef _WIN32 int is_windows_drive(const char *filename); #endif @@ -785,6 +803,9 @@ bool bdrv_requests_pending(BlockDriverState *bs); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); +void bdrv_inc_in_flight(BlockDriverState *bs); +void bdrv_dec_in_flight(BlockDriverState *bs); + void blockdev_close_all_bdrv_states(void); #endif /* BLOCK_INT_H */ diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 4ddb4ae2e1..2bb39f4d29 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -92,6 +92,13 @@ typedef struct BlockJobDriver { * besides job->blk to the new AioContext. */ void (*attached_aio_context)(BlockJob *job, AioContext *new_context); + + /* + * If the callback is not NULL, it will be invoked when the job has to be + * synchronously cancelled or completed; it should drain BlockDriverStates + * as required to ensure progress. + */ + void (*drain)(BlockJob *job); } BlockJobDriver; /** diff --git a/include/glib-compat.h b/include/glib-compat.h index 8093163bee..3f8370b3e4 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -304,4 +304,15 @@ static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func) } #endif +#if !GLIB_CHECK_VERSION(2, 26, 0) +static inline void g_source_set_name(GSource *source, const char *name) +{ + /* This is just a debugging aid, so leaving it a no-op */ +} +static inline void g_source_set_name_by_id(guint tag, const char *name) +{ + /* This is just a debugging aid, so leaving it a no-op */ +} +#endif + #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 17fff80c8a..98dc7722c3 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -13,7 +13,6 @@ #include "qemu/bitmap.h" #include "sysemu/sysemu.h" #include "hw/pci/pci.h" -#include "hw/boards.h" #include "hw/compat.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" diff --git a/include/hw/nvram/chrp_nvram.h b/include/hw/nvram/chrp_nvram.h new file mode 100644 index 0000000000..b4f5b2b104 --- /dev/null +++ b/include/hw/nvram/chrp_nvram.h @@ -0,0 +1,54 @@ +/* + * Common Hardware Reference Platform NVRAM functions. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef CHRP_NVRAM_H +#define CHRP_NVRAM_H + +/* OpenBIOS NVRAM partition */ +typedef struct { + uint8_t signature; + uint8_t checksum; + uint16_t len; /* Big endian, length divided by 16 */ + char name[12]; +} ChrpNvramPartHdr; + +#define CHRP_NVPART_SYSTEM 0x70 +#define CHRP_NVPART_FREE 0x7f + +static inline void +chrp_nvram_finish_partition(ChrpNvramPartHdr *header, uint32_t size) +{ + unsigned int i, sum; + uint8_t *tmpptr; + + /* Length divided by 16 */ + header->len = cpu_to_be16(size >> 4); + + /* Checksum */ + tmpptr = (uint8_t *)header; + sum = *tmpptr; + for (i = 0; i < 14; i++) { + sum += tmpptr[2 + i]; + sum = (sum + ((sum & 0xff00) >> 8)) & 0xff; + } + header->checksum = sum & 0xff; +} + +int chrp_nvram_create_system_partition(uint8_t *data, int min_len); +int chrp_nvram_create_free_partition(uint8_t *data, int len); + +#endif diff --git a/include/hw/nvram/openbios_firmware_abi.h b/include/hw/nvram/sun_nvram.h index 74cfd56180..68eaa60308 100644 --- a/include/hw/nvram/openbios_firmware_abi.h +++ b/include/hw/nvram/sun_nvram.h @@ -1,46 +1,5 @@ -#ifndef OPENBIOS_FIRMWARE_ABI_H -#define OPENBIOS_FIRMWARE_ABI_H - -/* OpenBIOS NVRAM partition */ -struct OpenBIOS_nvpart_v1 { - uint8_t signature; - uint8_t checksum; - uint16_t len; // BE, length divided by 16 - char name[12]; -}; - -#define OPENBIOS_PART_SYSTEM 0x70 -#define OPENBIOS_PART_FREE 0x7f - -static inline void -OpenBIOS_finish_partition(struct OpenBIOS_nvpart_v1 *header, uint32_t size) -{ - unsigned int i, sum; - uint8_t *tmpptr; - - // Length divided by 16 - header->len = cpu_to_be16(size >> 4); - - // Checksum - tmpptr = (uint8_t *)header; - sum = *tmpptr; - for (i = 0; i < 14; i++) { - sum += tmpptr[2 + i]; - sum = (sum + ((sum & 0xff00) >> 8)) & 0xff; - } - header->checksum = sum & 0xff; -} - -static inline uint32_t -OpenBIOS_set_var(uint8_t *nvram, uint32_t addr, const char *str) -{ - uint32_t len; - - len = strlen(str) + 1; - memcpy(&nvram[addr], str, len); - - return addr + len; -} +#ifndef SUN_NVRAM_H +#define SUN_NVRAM_H /* Sun IDPROM structure at the end of NVRAM */ /* from http://www.squirrel.com/squirrel/sun-nvram-hostid.faq.html */ @@ -72,4 +31,4 @@ Sun_init_header(struct Sun_nvram *header, const uint8_t *macaddr, int machine_id header->checksum = tmp; } -#endif /* OPENBIOS_FIRMWARE_ABI_H */ +#endif /* SUN_NVRAM_H */ diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h new file mode 100644 index 0000000000..02ac1c5f42 --- /dev/null +++ b/include/hw/ppc/pnv.h @@ -0,0 +1,129 @@ +/* + * QEMU PowerPC PowerNV various definitions + * + * Copyright (c) 2014-2016 BenH, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_H +#define _PPC_PNV_H + +#include "hw/boards.h" +#include "hw/sysbus.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_lpc.h" + +#define TYPE_PNV_CHIP "powernv-chip" +#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) +#define PNV_CHIP_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvChipClass, (klass), TYPE_PNV_CHIP) +#define PNV_CHIP_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP) + +typedef enum PnvChipType { + PNV_CHIP_POWER8E, /* AKA Murano (default) */ + PNV_CHIP_POWER8, /* AKA Venice */ + PNV_CHIP_POWER8NVL, /* AKA Naples */ + PNV_CHIP_POWER9, /* AKA Nimbus */ +} PnvChipType; + +typedef struct PnvChip { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + uint32_t chip_id; + uint64_t ram_start; + uint64_t ram_size; + + uint32_t nr_cores; + uint64_t cores_mask; + void *cores; + + hwaddr xscom_base; + MemoryRegion xscom_mmio; + MemoryRegion xscom; + AddressSpace xscom_as; + + PnvLpcController lpc; +} PnvChip; + +typedef struct PnvChipClass { + /*< private >*/ + SysBusDeviceClass parent_class; + + /*< public >*/ + const char *cpu_model; + PnvChipType chip_type; + uint64_t chip_cfam_id; + uint64_t cores_mask; + + hwaddr xscom_base; + + uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); +} PnvChipClass; + +#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E" +#define PNV_CHIP_POWER8E(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8E) + +#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8" +#define PNV_CHIP_POWER8(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8) + +#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL" +#define PNV_CHIP_POWER8NVL(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8NVL) + +#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9" +#define PNV_CHIP_POWER9(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) + +/* + * This generates a HW chip id depending on an index: + * + * 0x0, 0x1, 0x10, 0x11 + * + * 4 chips should be the maximum + */ +#define PNV_CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1)) + +#define TYPE_POWERNV_MACHINE MACHINE_TYPE_NAME("powernv") +#define POWERNV_MACHINE(obj) \ + OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) + +typedef struct PnvMachineState { + /*< private >*/ + MachineState parent_obj; + + uint32_t initrd_base; + long initrd_size; + + uint32_t num_chips; + PnvChip **chips; + + ISABus *isa_bus; +} PnvMachineState; + +#define PNV_FDT_ADDR 0x01000000 +#define PNV_TIMEBASE_FREQ 512000000ULL + +/* + * POWER8 MMIO base addresses + */ +#define PNV_XSCOM_SIZE 0x800000000ull +#define PNV_XSCOM_BASE(chip) \ + (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) + +#endif /* _PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h new file mode 100644 index 0000000000..2955a41c90 --- /dev/null +++ b/include/hw/ppc/pnv_core.h @@ -0,0 +1,50 @@ +/* + * QEMU PowerPC PowerNV CPU Core model + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_CORE_H +#define _PPC_PNV_CORE_H + +#include "hw/cpu/core.h" + +#define TYPE_PNV_CORE "powernv-cpu-core" +#define PNV_CORE(obj) \ + OBJECT_CHECK(PnvCore, (obj), TYPE_PNV_CORE) +#define PNV_CORE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvCoreClass, (klass), TYPE_PNV_CORE) +#define PNV_CORE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvCoreClass, (obj), TYPE_PNV_CORE) + +typedef struct PnvCore { + /*< private >*/ + CPUCore parent_obj; + + /*< public >*/ + void *threads; + uint32_t pir; + + MemoryRegion xscom_regs; +} PnvCore; + +typedef struct PnvCoreClass { + DeviceClass parent_class; + ObjectClass *cpu_oc; +} PnvCoreClass; + +extern char *pnv_core_typename(const char *model); + +#endif /* _PPC_PNV_CORE_H */ diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h new file mode 100644 index 0000000000..38e5506975 --- /dev/null +++ b/include/hw/ppc/pnv_lpc.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC PowerNV LPC controller + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_LPC_H +#define _PPC_PNV_LPC_H + +#define TYPE_PNV_LPC "pnv-lpc" +#define PNV_LPC(obj) \ + OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) + +typedef struct PnvLpcController { + DeviceState parent; + + uint64_t eccb_stat_reg; + uint32_t eccb_data_reg; + + /* OPB bus */ + MemoryRegion opb_mr; + AddressSpace opb_as; + + /* ISA IO and Memory space */ + MemoryRegion isa_io; + MemoryRegion isa_mem; + + /* Windows from OPB to ISA (aliases) */ + MemoryRegion opb_isa_io; + MemoryRegion opb_isa_mem; + MemoryRegion opb_isa_fw; + + /* Registers */ + MemoryRegion lpc_hc_regs; + MemoryRegion opb_master_regs; + + /* OPB Master LS registers */ + uint32_t opb_irq_stat; + uint32_t opb_irq_mask; + uint32_t opb_irq_pol; + uint32_t opb_irq_input; + + /* LPC HC registers */ + uint32_t lpc_hc_fw_seg_idsel; + uint32_t lpc_hc_fw_rd_acc_size; + uint32_t lpc_hc_irqser_ctrl; + uint32_t lpc_hc_irqmask; + uint32_t lpc_hc_irqstat; + uint32_t lpc_hc_error_addr; + + /* XSCOM registers */ + MemoryRegion xscom_regs; +} PnvLpcController; + +#endif /* _PPC_PNV_LPC_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h new file mode 100644 index 0000000000..c0a2fbb9f6 --- /dev/null +++ b/include/hw/ppc/pnv_xscom.h @@ -0,0 +1,78 @@ +/* + * QEMU PowerPC PowerNV XSCOM bus definitions + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_XSCOM_H +#define _PPC_PNV_XSCOM_H + +#include "qom/object.h" + +typedef struct PnvChip PnvChip; + +typedef struct PnvXScomInterface { + Object parent; +} PnvXScomInterface; + +#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" +#define PNV_XSCOM_INTERFACE(obj) \ + OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE) +#define PNV_XSCOM_INTERFACE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \ + TYPE_PNV_XSCOM_INTERFACE) +#define PNV_XSCOM_INTERFACE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvXScomInterfaceClass, (obj), TYPE_PNV_XSCOM_INTERFACE) + +typedef struct PnvXScomInterfaceClass { + InterfaceClass parent; + int (*populate)(PnvXScomInterface *dev, void *fdt, int offset); +} PnvXScomInterfaceClass; + +/* + * Layout of the XSCOM PCB addresses of EX core 1 + * + * GPIO 0x1100xxxx + * SCOM 0x1101xxxx + * OHA 0x1102xxxx + * CLOCK CTL 0x1103xxxx + * FIR 0x1104xxxx + * THERM 0x1105xxxx + * <reserved> 0x1106xxxx + * .. + * 0x110Exxxx + * PCB SLAVE 0x110Fxxxx + */ + +#define PNV_XSCOM_EX_BASE 0x10000000 +#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24)) +#define PNV_XSCOM_EX_CORE_SIZE 0x100000 + +#define PNV_XSCOM_LPC_BASE 0xb0020 +#define PNV_XSCOM_LPC_SIZE 0x4 + +extern void pnv_xscom_realize(PnvChip *chip, Error **errp); +extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); + +extern void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, + MemoryRegion *mr); +extern void pnv_xscom_region_init(MemoryRegion *mr, + struct Object *owner, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); + +#endif /* _PPC_PNV_XSCOM_H */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index aeaba3edf9..bd5bcf70de 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -6,12 +6,14 @@ #include "hw/ppc/xics.h" #include "hw/ppc/spapr_drc.h" #include "hw/mem/pc-dimm.h" +#include "hw/ppc/spapr_ovec.h" struct VIOsPAPRBus; struct sPAPRPHBState; struct sPAPRNVRAM; typedef struct sPAPRConfigureConnectorState sPAPRConfigureConnectorState; typedef struct sPAPREventLogEntry sPAPREventLogEntry; +typedef struct sPAPREventSource sPAPREventSource; #define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL #define SPAPR_ENTRY_POINT 0x100 @@ -63,17 +65,23 @@ struct sPAPRMachineState { uint32_t htab_shift; hwaddr rma_size; int vrma_adjust; - hwaddr fdt_addr, rtas_addr; ssize_t rtas_size; void *rtas_blob; - void *fdt_skel; + long kernel_size; + bool kernel_le; + uint32_t initrd_base; + long initrd_size; uint64_t rtc_offset; /* Now used only during incoming migration */ struct PPCTimebase tb; bool has_graphics; + sPAPROptionVector *ov5; /* QEMU-supported option vectors */ + sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ + bool cas_reboot; - uint32_t check_exception_irq; Notifier epow_notifier; QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; + bool use_hotplug_event_source; + sPAPREventSource *event_sources; /* Migration state */ int htab_save_index; @@ -527,8 +535,8 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn); target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *sm, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets); -int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, - hwaddr rtas_size); +void spapr_dt_rtas_tokens(void *fdt, int rtas); +void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_TCE_PAGE_SHIFT 12 #define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT) @@ -578,10 +586,11 @@ struct sPAPREventLogEntry { }; void spapr_events_init(sPAPRMachineState *sm); -void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); +void spapr_dt_events(sPAPRMachineState *sm, void *fdt); int spapr_h_cas_compose_response(sPAPRMachineState *sm, target_ulong addr, target_ulong size, - bool cpu_update, bool memory_update); + bool cpu_update, + sPAPROptionVector *ov5_updates); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(sPAPRTCETable *tcet, uint32_t page_shift, uint64_t bus_offset, @@ -601,6 +610,10 @@ void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, uint32_t count); void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, uint32_t count); +void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index); +void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index); void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp); void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, sPAPRMachineState *spapr); diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h new file mode 100644 index 0000000000..6a06da32e6 --- /dev/null +++ b/include/hw/ppc/spapr_ovec.h @@ -0,0 +1,67 @@ +/* + * QEMU SPAPR Option/Architecture Vector Definitions + * + * Each architecture option is organized/documented by the following + * in LoPAPR 1.1, Table 244: + * + * <vector number>: the bit-vector in which the option is located + * <vector byte>: the byte offset of the vector entry + * <vector bit>: the bit offset within the vector entry + * + * where each vector entry can be one or more bytes. + * + * Firmware expects a somewhat literal encoding of this bit-vector + * structure, where each entry is stored in little-endian so that the + * byte ordering reflects that of the documentation, but where each bit + * offset is from "left-to-right" in the traditional representation of + * a byte value where the MSB is the left-most bit. Thus, each + * individual byte encodes the option bits in reverse order of the + * documented bit. + * + * These definitions/helpers attempt to abstract away this internal + * representation so that we can define/set/test for individual option + * bits using only the documented values. This is done mainly by relying + * on a bitmap to approximate the documented "bit-vector" structure and + * handling conversations to-from the internal representation under the + * covers. + * + * Copyright IBM Corp. 2016 + * + * Authors: + * Michael Roth <mdroth@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef _SPAPR_OVEC_H +#define _SPAPR_OVEC_H + +#include "cpu.h" + +typedef struct sPAPROptionVector sPAPROptionVector; + +#define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) + +/* option vector 5 */ +#define OV5_DRCONF_MEMORY OV_BIT(2, 2) +#define OV5_FORM1_AFFINITY OV_BIT(5, 0) +#define OV5_HP_EVT OV_BIT(6, 5) + +/* interfaces */ +sPAPROptionVector *spapr_ovec_new(void); +sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); +void spapr_ovec_intersect(sPAPROptionVector *ov, + sPAPROptionVector *ov1, + sPAPROptionVector *ov2); +bool spapr_ovec_diff(sPAPROptionVector *ov, + sPAPROptionVector *ov_old, + sPAPROptionVector *ov_new); +void spapr_ovec_cleanup(sPAPROptionVector *ov); +void spapr_ovec_set(sPAPROptionVector *ov, long bitnr); +void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr); +bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr); +sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); +int spapr_ovec_populate_dt(void *fdt, int fdt_offset, + sPAPROptionVector *ov, const char *name); + +#endif /* !defined (_SPAPR_OVEC_H) */ diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 40d0e5f6a3..14f502240e 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -76,14 +76,12 @@ struct VIOsPAPRDevice { struct VIOsPAPRBus { BusState bus; uint32_t next_reg; - int (*init)(VIOsPAPRDevice *dev); - int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); }; extern VIOsPAPRBus *spapr_vio_bus_init(void); extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg); -extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt); -extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus); +void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt); +extern gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus); static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev) { diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 66ae55ded3..3f0c31610a 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -117,6 +117,8 @@ struct ICPState { uint8_t mfrr; qemu_irq output; bool cap_irq_xics_enabled; + + XICSState *xics; }; #define TYPE_ICS_BASE "ics-base" @@ -185,18 +187,21 @@ int xics_spapr_alloc(XICSState *icp, int irq_hint, bool lsi, Error **errp); int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align, Error **errp); void xics_spapr_free(XICSState *icp, int irq, int num); +void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle); void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu); void xics_cpu_destroy(XICSState *icp, PowerPCCPU *cpu); +void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, + const char *typename, Error **errp); /* Internal XICS interfaces */ int xics_get_cpu_index_by_dt_id(int cpu_dt_id); -void icp_set_cppr(XICSState *icp, int server, uint8_t cppr); -void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr); +void icp_set_cppr(ICPState *icp, uint8_t cppr); +void icp_set_mfrr(ICPState *icp, uint8_t mfrr); uint32_t icp_accept(ICPState *ss); uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr); -void icp_eoi(XICSState *icp, int server, uint32_t xirr); +void icp_eoi(ICPState *icp, uint32_t xirr); void ics_simple_write_xive(ICSState *ics, int nr, int server, uint8_t priority, uint8_t saved_priority); diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 0df282ab5f..cbda40ee53 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -2,60 +2,10 @@ #define QEMU_HW_XEN_BACKEND_H #include "hw/xen/xen_common.h" +#include "hw/xen/xen_pvdev.h" #include "sysemu/sysemu.h" #include "net/net.h" -/* ------------------------------------------------------------- */ - -#define XEN_BUFSIZE 1024 - -struct XenDevice; - -/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ -#define DEVOPS_FLAG_NEED_GNTDEV 1 -/* don't expect frontend doing correct state transitions (aka console quirk) */ -#define DEVOPS_FLAG_IGNORE_STATE 2 - -struct XenDevOps { - size_t size; - uint32_t flags; - void (*alloc)(struct XenDevice *xendev); - int (*init)(struct XenDevice *xendev); - int (*initialise)(struct XenDevice *xendev); - void (*connected)(struct XenDevice *xendev); - void (*event)(struct XenDevice *xendev); - void (*disconnect)(struct XenDevice *xendev); - int (*free)(struct XenDevice *xendev); - void (*backend_changed)(struct XenDevice *xendev, const char *node); - void (*frontend_changed)(struct XenDevice *xendev, const char *node); - int (*backend_register)(void); -}; - -struct XenDevice { - const char *type; - int dom; - int dev; - char name[64]; - int debug; - - enum xenbus_state be_state; - enum xenbus_state fe_state; - int online; - char be[XEN_BUFSIZE]; - char *fe; - char *protocol; - int remote_port; - int local_port; - - xenevtchn_handle *evtchndev; - xengnttab_handle *gnttabdev; - - struct XenDevOps *ops; - QTAILQ_ENTRY(XenDevice) next; -}; - -/* ------------------------------------------------------------- */ - /* variables */ extern xc_interface *xen_xc; extern xenforeignmemory_handle *xen_fmem; @@ -63,26 +13,20 @@ extern struct xs_handle *xenstore; extern const char *xen_protocol; extern DeviceState *xen_sysdev; -/* xenstore helper functions */ int xenstore_mkdir(char *path, int p); -int xenstore_write_str(const char *base, const char *node, const char *val); -int xenstore_write_int(const char *base, const char *node, int ival); -int xenstore_write_int64(const char *base, const char *node, int64_t ival); -char *xenstore_read_str(const char *base, const char *node); -int xenstore_read_int(const char *base, const char *node, int *ival); - int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival); char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +void xenstore_update_fe(char *watch, struct XenDevice *xendev); +void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops); char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); -int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval); -int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, uint64_t *uval); +int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, + uint64_t *uval); -const char *xenbus_strstate(enum xenbus_state state); -struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev); void xen_be_check_state(struct XenDevice *xendev); /* xen backend driver bits */ @@ -91,10 +35,6 @@ void xen_be_register_common(void); int xen_be_register(const char *type, struct XenDevOps *ops); int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenDevice *xendev); -void xen_be_unbind_evtchn(struct XenDevice *xendev); -int xen_be_send_notify(struct XenDevice *xendev); -void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) - GCC_FMT_ATTR(3, 4); /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h new file mode 100644 index 0000000000..083f0a9cc7 --- /dev/null +++ b/include/hw/xen/xen_pvdev.h @@ -0,0 +1,78 @@ +#ifndef QEMU_HW_XEN_PVDEV_H +#define QEMU_HW_XEN_PVDEV_H + +#include "hw/xen/xen_common.h" +/* ------------------------------------------------------------- */ + +#define XEN_BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don't expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*initialise)(struct XenDevice *xendev); + void (*connected)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); + int (*backend_register)(void); +}; + +struct XenDevice { + const char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[XEN_BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + xenevtchn_handle *evtchndev; + xengnttab_handle *gnttabdev; + + struct XenDevOps *ops; + QTAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +int xenstore_write_int64(const char *base, const char *node, int64_t ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); +int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval); +void xenstore_update(void *unused); + +const char *xenbus_strstate(enum xenbus_state state); + +void xen_pv_evtchn_event(void *opaque); +void xen_pv_insert_xendev(struct XenDevice *xendev); +void xen_pv_del_xendev(struct XenDevice *xendev); +struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev); + +void xen_pv_unbind_evtchn(struct XenDevice *xendev); +int xen_pv_send_notify(struct XenDevice *xendev); + +void xen_pv_printf(struct XenDevice *xendev, int msg_level, + const char *fmt, ...) GCC_FMT_ATTR(3, 4); + +#endif /* QEMU_HW_XEN_PVDEV_H */ diff --git a/include/io/channel.h b/include/io/channel.h index 752e89f4dc..32a9470794 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -40,9 +40,9 @@ typedef struct QIOChannelClass QIOChannelClass; typedef enum QIOChannelFeature QIOChannelFeature; enum QIOChannelFeature { - QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0), - QIO_CHANNEL_FEATURE_SHUTDOWN = (1 << 1), - QIO_CHANNEL_FEATURE_LISTEN = (1 << 2), + QIO_CHANNEL_FEATURE_FD_PASS, + QIO_CHANNEL_FEATURE_SHUTDOWN, + QIO_CHANNEL_FEATURE_LISTEN, }; @@ -79,6 +79,7 @@ typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc, struct QIOChannel { Object parent; unsigned int features; /* bitmask of QIOChannelFeatures */ + char *name; #ifdef _WIN32 HANDLE event; /* For use with GSource on Win32 */ #endif @@ -149,6 +150,28 @@ bool qio_channel_has_feature(QIOChannel *ioc, QIOChannelFeature feature); /** + * qio_channel_set_feature: + * @ioc: the channel object + * @feature: the feature to set support for + * + * Add channel support for the feature named in @feature. + */ +void qio_channel_set_feature(QIOChannel *ioc, + QIOChannelFeature feature); + +/** + * qio_channel_set_name: + * @ioc: the channel object + * @name: the name of the channel + * + * Sets the name of the channel, which serves as an aid + * to debugging. The name is used when creating GSource + * watches for this channel. + */ +void qio_channel_set_name(QIOChannel *ioc, + const char *name); + +/** * qio_channel_readv_full: * @ioc: the channel object * @iov: the array of memory regions to read data into diff --git a/include/migration/colo.h b/include/migration/colo.h new file mode 100644 index 0000000000..e32eef4763 --- /dev/null +++ b/include/migration/colo.h @@ -0,0 +1,38 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * 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 QEMU_COLO_H +#define QEMU_COLO_H + +#include "qemu-common.h" +#include "migration/migration.h" +#include "qemu/coroutine_int.h" +#include "qemu/thread.h" +#include "qemu/main-loop.h" + +bool colo_supported(void); +void colo_info_init(void); + +void migrate_start_colo_process(MigrationState *s); +bool migration_in_colo_state(void); + +/* loadvm */ +bool migration_incoming_enable_colo(void); +void migration_incoming_exit_colo(void); +void *colo_process_incoming_thread(void *opaque); +bool migration_incoming_in_colo_state(void); + +COLOMode get_colo_mode(void); + +/* failover */ +void colo_do_failover(MigrationState *s); +#endif diff --git a/include/migration/failover.h b/include/migration/failover.h new file mode 100644 index 0000000000..ad91ef2381 --- /dev/null +++ b/include/migration/failover.h @@ -0,0 +1,26 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO.,LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * 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 QEMU_FAILOVER_H +#define QEMU_FAILOVER_H + +#include "qemu-common.h" +#include "qapi-types.h" + +void failover_init_state(void); +FailoverStatus failover_set_state(FailoverStatus old_state, + FailoverStatus new_state); +FailoverStatus failover_get_state(void); +void failover_request_active(Error **errp); +bool failover_request_is_active(void); + +#endif diff --git a/include/migration/migration.h b/include/migration/migration.h index 2791b90c00..c309d23370 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -21,6 +21,7 @@ #include "migration/vmstate.h" #include "qapi-types.h" #include "exec/cpu-common.h" +#include "qemu/coroutine_int.h" #define QEMU_VM_FILE_MAGIC 0x5145564d #define QEMU_VM_FILE_VERSION_COMPAT 0x00000002 @@ -107,6 +108,12 @@ struct MigrationIncomingState { QEMUBH *bh; int state; + + bool have_colo_incoming_thread; + QemuThread colo_incoming_thread; + /* The coroutine we should enter (back) after failover */ + Coroutine *migration_incoming_co; + /* See savevm.c */ LoadStateEntry_Head loadvm_handlers; }; @@ -298,6 +305,7 @@ int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen); int migrate_use_xbzrle(void); int64_t migrate_xbzrle_cache_size(void); +bool migrate_colo_enabled(void); int64_t xbzrle_cache_resize(int64_t new_size); diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index a714d8ef80..8cc532ec0e 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -9,7 +9,7 @@ extern Monitor *cur_mon; /* flags for monitor_init */ -#define MONITOR_IS_DEFAULT 0x01 +/* 0x01 unused */ #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 #define MONITOR_USE_PRETTY 0x08 diff --git a/include/qemu/rfifolock.h b/include/qemu/rfifolock.h deleted file mode 100644 index b23ab538a6..0000000000 --- a/include/qemu/rfifolock.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Recursive FIFO lock - * - * Copyright Red Hat, Inc. 2013 - * - * Authors: - * Stefan Hajnoczi <stefanha@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef QEMU_RFIFOLOCK_H -#define QEMU_RFIFOLOCK_H - -#include "qemu/thread.h" - -/* Recursive FIFO lock - * - * This lock provides more features than a plain mutex: - * - * 1. Fairness - enforces FIFO order. - * 2. Nesting - can be taken recursively. - * 3. Contention callback - optional, called when thread must wait. - * - * The recursive FIFO lock is heavyweight so prefer other synchronization - * primitives if you do not need its features. - */ -typedef struct { - QemuMutex lock; /* protects all fields */ - - /* FIFO order */ - unsigned int head; /* active ticket number */ - unsigned int tail; /* waiting ticket number */ - QemuCond cond; /* used to wait for our ticket number */ - - /* Nesting */ - QemuThread owner_thread; /* thread that currently has ownership */ - unsigned int nesting; /* amount of nesting levels */ - - /* Contention callback */ - void (*cb)(void *); /* called when thread must wait, with ->lock - * held so it may not recursively lock/unlock - */ - void *cb_opaque; -} RFifoLock; - -void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque); -void rfifolock_destroy(RFifoLock *r); -void rfifolock_lock(RFifoLock *r); -void rfifolock_unlock(RFifoLock *r); - -#endif /* QEMU_RFIFOLOCK_H */ diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index aa03567e5e..09d1e15728 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -4,6 +4,12 @@ #include <pthread.h> #include <semaphore.h> +typedef QemuMutex QemuRecMutex; +#define qemu_rec_mutex_destroy qemu_mutex_destroy +#define qemu_rec_mutex_lock qemu_mutex_lock +#define qemu_rec_mutex_try_lock qemu_mutex_try_lock +#define qemu_rec_mutex_unlock qemu_mutex_unlock + struct QemuMutex { pthread_mutex_t lock; }; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index c7ce8dcd45..5fb6541ae9 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -8,6 +8,16 @@ struct QemuMutex { LONG owner; }; +typedef struct QemuRecMutex QemuRecMutex; +struct QemuRecMutex { + CRITICAL_SECTION lock; +}; + +void qemu_rec_mutex_destroy(QemuRecMutex *mutex); +void qemu_rec_mutex_lock(QemuRecMutex *mutex); +int qemu_rec_mutex_trylock(QemuRecMutex *mutex); +void qemu_rec_mutex_unlock(QemuRecMutex *mutex); + struct QemuCond { LONG waiters, target; HANDLE sema; diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 31237e93ee..e8e665f020 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -25,6 +25,9 @@ void qemu_mutex_lock(QemuMutex *mutex); int qemu_mutex_trylock(QemuMutex *mutex); void qemu_mutex_unlock(QemuMutex *mutex); +/* Prototypes for other functions are in thread-posix.h/thread-win32.h. */ +void qemu_rec_mutex_init(QemuRecMutex *mutex); + void qemu_cond_init(QemuCond *cond); void qemu_cond_destroy(QemuCond *cond); diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index b07159b639..6444e41d39 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -146,6 +146,7 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int count, BlockCompletionFunc *cb, void *opaque); void blk_aio_cancel(BlockAIOCB *acb); void blk_aio_cancel_async(BlockAIOCB *acb); +int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index 34c8eaf64e..c228c66513 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -199,14 +199,14 @@ typedef BlockAIOCB *DMAIOFunc(int64_t offset, QEMUIOVector *iov, void *opaque); BlockAIOCB *dma_blk_io(AioContext *ctx, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, void *opaque, DMADirection dir); BlockAIOCB *dma_blk_read(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *dma_blk_write(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, BlockCompletionFunc *cb, void *opaque); uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg); uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg); diff --git a/io/channel-socket.c b/io/channel-socket.c index 196a4f18f7..d7e03f6266 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -55,7 +55,7 @@ qio_channel_socket_new(void) sioc->fd = -1; ioc = QIO_CHANNEL(sioc); - ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); + qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); #ifdef WIN32 ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -72,9 +72,6 @@ qio_channel_socket_set_fd(QIOChannelSocket *sioc, int fd, Error **errp) { - int val; - socklen_t len = sizeof(val); - if (sioc->fd != -1) { error_setg(errp, "Socket is already open"); return -1; @@ -107,13 +104,9 @@ qio_channel_socket_set_fd(QIOChannelSocket *sioc, #ifndef WIN32 if (sioc->localAddr.ss_family == AF_UNIX) { QIOChannel *ioc = QIO_CHANNEL(sioc); - ioc->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS); + qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS); } #endif /* WIN32 */ - if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &val, &len) == 0 && val) { - QIOChannel *ioc = QIO_CHANNEL(sioc); - ioc->features |= (1 << QIO_CHANNEL_FEATURE_LISTEN); - } return 0; @@ -220,6 +213,7 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, close(fd); return -1; } + qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_LISTEN); return 0; } @@ -380,7 +374,8 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, #ifndef WIN32 if (cioc->localAddr.ss_family == AF_UNIX) { - QIO_CHANNEL(cioc)->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS); + QIOChannel *ioc_local = QIO_CHANNEL(cioc); + qio_channel_set_feature(ioc_local, QIO_CHANNEL_FEATURE_FD_PASS); } #endif /* WIN32 */ @@ -403,7 +398,8 @@ static void qio_channel_socket_finalize(Object *obj) QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); if (ioc->fd != -1) { - if (QIO_CHANNEL(ioc)->features & QIO_CHANNEL_FEATURE_LISTEN) { + QIOChannel *ioc_local = QIO_CHANNEL(ioc); + if (qio_channel_has_feature(ioc_local, QIO_CHANNEL_FEATURE_LISTEN)) { Error *err = NULL; socket_listen_cleanup(ioc->fd, &err); diff --git a/io/channel-tls.c b/io/channel-tls.c index 9a8525c816..d24dc8c613 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -111,8 +111,8 @@ qio_channel_tls_new_client(QIOChannel *master, ioc = QIO_CHANNEL(tioc); tioc->master = master; - if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) { - ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); + if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { + qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } object_ref(OBJECT(master)); diff --git a/io/channel-websock.c b/io/channel-websock.c index 533bd4b3b5..f45bced82a 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -497,8 +497,8 @@ qio_channel_websock_new_server(QIOChannel *master) ioc = QIO_CHANNEL(wioc); wioc->master = master; - if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) { - ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); + if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { + qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } object_ref(OBJECT(master)); diff --git a/io/channel.c b/io/channel.c index 923c4651ca..80924c1772 100644 --- a/io/channel.c +++ b/io/channel.c @@ -30,6 +30,21 @@ bool qio_channel_has_feature(QIOChannel *ioc, } +void qio_channel_set_feature(QIOChannel *ioc, + QIOChannelFeature feature) +{ + ioc->features |= (1 << feature); +} + + +void qio_channel_set_name(QIOChannel *ioc, + const char *name) +{ + g_free(ioc->name); + ioc->name = g_strdup(name); +} + + ssize_t qio_channel_readv_full(QIOChannel *ioc, const struct iovec *iov, size_t niov, @@ -40,7 +55,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc, QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); if ((fds || nfds) && - !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) { + !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { error_setg_errno(errp, EINVAL, "Channel does not support file descriptor passing"); return -1; @@ -60,7 +75,7 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); if ((fds || nfds) && - !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) { + !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { error_setg_errno(errp, EINVAL, "Channel does not support file descriptor passing"); return -1; @@ -129,7 +144,13 @@ GSource *qio_channel_create_watch(QIOChannel *ioc, GIOCondition condition) { QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); - return klass->io_create_watch(ioc, condition); + GSource *ret = klass->io_create_watch(ioc, condition); + + if (ioc->name) { + g_source_set_name(ret, ioc->name); + } + + return ret; } @@ -275,24 +296,24 @@ void qio_channel_wait(QIOChannel *ioc, } -#ifdef _WIN32 static void qio_channel_finalize(Object *obj) { QIOChannel *ioc = QIO_CHANNEL(obj); + g_free(ioc->name); + +#ifdef _WIN32 if (ioc->event) { CloseHandle(ioc->event); } -} #endif +} static const TypeInfo qio_channel_info = { .parent = TYPE_OBJECT, .name = TYPE_QIO_CHANNEL, .instance_size = sizeof(QIOChannel), -#ifdef _WIN32 .instance_finalize = qio_channel_finalize, -#endif .abstract = true, .class_size = sizeof(QIOChannelClass), }; diff --git a/iothread.c b/iothread.c index fbeb8deb38..bd70344811 100644 --- a/iothread.c +++ b/iothread.c @@ -16,10 +16,12 @@ #include "qom/object_interfaces.h" #include "qemu/module.h" #include "block/aio.h" +#include "block/block.h" #include "sysemu/iothread.h" #include "qmp-commands.h" #include "qemu/error-report.h" #include "qemu/rcu.h" +#include "qemu/main-loop.h" typedef ObjectClass IOThreadClass; @@ -28,26 +30,27 @@ typedef ObjectClass IOThreadClass; #define IOTHREAD_CLASS(klass) \ OBJECT_CLASS_CHECK(IOThreadClass, klass, TYPE_IOTHREAD) +static __thread IOThread *my_iothread; + +AioContext *qemu_get_current_aio_context(void) +{ + return my_iothread ? my_iothread->ctx : qemu_get_aio_context(); +} + static void *iothread_run(void *opaque) { IOThread *iothread = opaque; - bool blocking; rcu_register_thread(); + my_iothread = iothread; qemu_mutex_lock(&iothread->init_done_lock); iothread->thread_id = qemu_get_thread_id(); qemu_cond_signal(&iothread->init_done_cond); qemu_mutex_unlock(&iothread->init_done_lock); - while (!iothread->stopping) { - aio_context_acquire(iothread->ctx); - blocking = true; - while (!iothread->stopping && aio_poll(iothread->ctx, blocking)) { - /* Progress was made, keep going */ - blocking = false; - } - aio_context_release(iothread->ctx); + while (!atomic_read(&iothread->stopping)) { + aio_poll(iothread->ctx, true); } rcu_unregister_thread(); @@ -190,6 +193,18 @@ IOThreadInfoList *qmp_query_iothreads(Error **errp) void iothread_stop_all(void) { Object *container = object_get_objects_root(); + BlockDriverState *bs; + BdrvNextIterator it; + + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + AioContext *ctx = bdrv_get_aio_context(bs); + if (ctx == qemu_get_aio_context()) { + continue; + } + aio_context_acquire(ctx); + bdrv_set_aio_context(bs, qemu_get_aio_context()); + aio_context_release(ctx); + } object_child_foreach(container, iothread_stop, NULL); } diff --git a/main-loop.c b/main-loop.c index 6a7f8d30bd..66c4eb69a3 100644 --- a/main-loop.c +++ b/main-loop.c @@ -161,9 +161,11 @@ int qemu_init_main_loop(Error **errp) qemu_notify_bh = qemu_bh_new(notify_event_cb, NULL); gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); src = aio_get_g_source(qemu_aio_context); + g_source_set_name(src, "aio-context"); g_source_attach(src, NULL); g_source_unref(src); src = iohandler_get_g_source(); + g_source_set_name(src, "io-handler"); g_source_attach(src, NULL); g_source_unref(src); return 0; diff --git a/migration/Makefile.objs b/migration/Makefile.objs index 30ad945918..3f3e237142 100644 --- a/migration/Makefile.objs +++ b/migration/Makefile.objs @@ -1,5 +1,7 @@ common-obj-y += migration.o socket.o fd.o exec.o common-obj-y += tls.o +common-obj-y += colo-comm.o +common-obj-$(CONFIG_COLO) += colo.o colo-failover.o common-obj-y += vmstate.o common-obj-y += qemu-file.o common-obj-y += qemu-file-channel.o diff --git a/migration/colo-comm.c b/migration/colo-comm.c new file mode 100644 index 0000000000..20b60ec384 --- /dev/null +++ b/migration/colo-comm.c @@ -0,0 +1,72 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include <migration/colo.h> +#include "trace.h" + +typedef struct { + bool colo_requested; +} COLOInfo; + +static COLOInfo colo_info; + +COLOMode get_colo_mode(void) +{ + if (migration_in_colo_state()) { + return COLO_MODE_PRIMARY; + } else if (migration_incoming_in_colo_state()) { + return COLO_MODE_SECONDARY; + } else { + return COLO_MODE_UNKNOWN; + } +} + +static void colo_info_pre_save(void *opaque) +{ + COLOInfo *s = opaque; + + s->colo_requested = migrate_colo_enabled(); +} + +static bool colo_info_need(void *opaque) +{ + return migrate_colo_enabled(); +} + +static const VMStateDescription colo_state = { + .name = "COLOState", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = colo_info_pre_save, + .needed = colo_info_need, + .fields = (VMStateField[]) { + VMSTATE_BOOL(colo_requested, COLOInfo), + VMSTATE_END_OF_LIST() + }, +}; + +void colo_info_init(void) +{ + vmstate_register(NULL, 0, &colo_state, &colo_info); +} + +bool migration_incoming_enable_colo(void) +{ + return colo_info.colo_requested; +} + +void migration_incoming_exit_colo(void) +{ + colo_info.colo_requested = false; +} diff --git a/migration/colo-failover.c b/migration/colo-failover.c new file mode 100644 index 0000000000..cc229f5ab1 --- /dev/null +++ b/migration/colo-failover.c @@ -0,0 +1,83 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "migration/colo.h" +#include "migration/failover.h" +#include "qmp-commands.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "trace.h" + +static QEMUBH *failover_bh; +static FailoverStatus failover_state; + +static void colo_failover_bh(void *opaque) +{ + int old_state; + + qemu_bh_delete(failover_bh); + failover_bh = NULL; + + old_state = failover_set_state(FAILOVER_STATUS_REQUIRE, + FAILOVER_STATUS_ACTIVE); + if (old_state != FAILOVER_STATUS_REQUIRE) { + error_report("Unknown error for failover, old_state = %s", + FailoverStatus_lookup[old_state]); + return; + } + + colo_do_failover(NULL); +} + +void failover_request_active(Error **errp) +{ + if (failover_set_state(FAILOVER_STATUS_NONE, + FAILOVER_STATUS_REQUIRE) != FAILOVER_STATUS_NONE) { + error_setg(errp, "COLO failover is already actived"); + return; + } + failover_bh = qemu_bh_new(colo_failover_bh, NULL); + qemu_bh_schedule(failover_bh); +} + +void failover_init_state(void) +{ + failover_state = FAILOVER_STATUS_NONE; +} + +FailoverStatus failover_set_state(FailoverStatus old_state, + FailoverStatus new_state) +{ + FailoverStatus old; + + old = atomic_cmpxchg(&failover_state, old_state, new_state); + if (old == old_state) { + trace_colo_failover_set_state(FailoverStatus_lookup[new_state]); + } + return old; +} + +FailoverStatus failover_get_state(void) +{ + return atomic_read(&failover_state); +} + +void qmp_x_colo_lost_heartbeat(Error **errp) +{ + if (get_colo_mode() == COLO_MODE_UNKNOWN) { + error_setg(errp, QERR_FEATURE_DISABLED, "colo"); + return; + } + + failover_request_active(errp); +} diff --git a/migration/colo.c b/migration/colo.c new file mode 100644 index 0000000000..e7224b8a0a --- /dev/null +++ b/migration/colo.c @@ -0,0 +1,529 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "migration/colo.h" +#include "io/channel-buffer.h" +#include "trace.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "migration/failover.h" + +#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024) + +bool colo_supported(void) +{ + return true; +} + +bool migration_in_colo_state(void) +{ + MigrationState *s = migrate_get_current(); + + return (s->state == MIGRATION_STATUS_COLO); +} + +bool migration_incoming_in_colo_state(void) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + return mis && (mis->state == MIGRATION_STATUS_COLO); +} + +static bool colo_runstate_is_stopped(void) +{ + return runstate_check(RUN_STATE_COLO) || !runstate_is_running(); +} + +static void secondary_vm_do_failover(void) +{ + int old_state; + MigrationIncomingState *mis = migration_incoming_get_current(); + + migrate_set_state(&mis->state, MIGRATION_STATUS_COLO, + MIGRATION_STATUS_COMPLETED); + + if (!autostart) { + error_report("\"-S\" qemu option will be ignored in secondary side"); + /* recover runstate to normal migration finish state */ + autostart = true; + } + + old_state = failover_set_state(FAILOVER_STATUS_ACTIVE, + FAILOVER_STATUS_COMPLETED); + if (old_state != FAILOVER_STATUS_ACTIVE) { + error_report("Incorrect state (%s) while doing failover for " + "secondary VM", FailoverStatus_lookup[old_state]); + return; + } + /* For Secondary VM, jump to incoming co */ + if (mis->migration_incoming_co) { + qemu_coroutine_enter(mis->migration_incoming_co); + } +} + +static void primary_vm_do_failover(void) +{ + MigrationState *s = migrate_get_current(); + int old_state; + + migrate_set_state(&s->state, MIGRATION_STATUS_COLO, + MIGRATION_STATUS_COMPLETED); + + old_state = failover_set_state(FAILOVER_STATUS_ACTIVE, + FAILOVER_STATUS_COMPLETED); + if (old_state != FAILOVER_STATUS_ACTIVE) { + error_report("Incorrect state (%s) while doing failover for Primary VM", + FailoverStatus_lookup[old_state]); + return; + } +} + +void colo_do_failover(MigrationState *s) +{ + /* Make sure VM stopped while failover happened. */ + if (!colo_runstate_is_stopped()) { + vm_stop_force_state(RUN_STATE_COLO); + } + + if (get_colo_mode() == COLO_MODE_PRIMARY) { + primary_vm_do_failover(); + } else { + secondary_vm_do_failover(); + } +} + +static void colo_send_message(QEMUFile *f, COLOMessage msg, + Error **errp) +{ + int ret; + + if (msg >= COLO_MESSAGE__MAX) { + error_setg(errp, "%s: Invalid message", __func__); + return; + } + qemu_put_be32(f, msg); + qemu_fflush(f); + + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "Can't send COLO message"); + } + trace_colo_send_message(COLOMessage_lookup[msg]); +} + +static void colo_send_message_value(QEMUFile *f, COLOMessage msg, + uint64_t value, Error **errp) +{ + Error *local_err = NULL; + int ret; + + colo_send_message(f, msg, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + qemu_put_be64(f, value); + qemu_fflush(f); + + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to send value for message:%s", + COLOMessage_lookup[msg]); + } +} + +static COLOMessage colo_receive_message(QEMUFile *f, Error **errp) +{ + COLOMessage msg; + int ret; + + msg = qemu_get_be32(f); + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "Can't receive COLO message"); + return msg; + } + if (msg >= COLO_MESSAGE__MAX) { + error_setg(errp, "%s: Invalid message", __func__); + return msg; + } + trace_colo_receive_message(COLOMessage_lookup[msg]); + return msg; +} + +static void colo_receive_check_message(QEMUFile *f, COLOMessage expect_msg, + Error **errp) +{ + COLOMessage msg; + Error *local_err = NULL; + + msg = colo_receive_message(f, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (msg != expect_msg) { + error_setg(errp, "Unexpected COLO message %d, expected %d", + msg, expect_msg); + } +} + +static uint64_t colo_receive_message_value(QEMUFile *f, uint32_t expect_msg, + Error **errp) +{ + Error *local_err = NULL; + uint64_t value; + int ret; + + colo_receive_check_message(f, expect_msg, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return 0; + } + + value = qemu_get_be64(f); + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get value for COLO message: %s", + COLOMessage_lookup[expect_msg]); + } + return value; +} + +static int colo_do_checkpoint_transaction(MigrationState *s, + QIOChannelBuffer *bioc, + QEMUFile *fb) +{ + Error *local_err = NULL; + int ret = -1; + + colo_send_message(s->to_dst_file, COLO_MESSAGE_CHECKPOINT_REQUEST, + &local_err); + if (local_err) { + goto out; + } + + colo_receive_check_message(s->rp_state.from_dst_file, + COLO_MESSAGE_CHECKPOINT_REPLY, &local_err); + if (local_err) { + goto out; + } + /* Reset channel-buffer directly */ + qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); + bioc->usage = 0; + + qemu_mutex_lock_iothread(); + if (failover_get_state() != FAILOVER_STATUS_NONE) { + qemu_mutex_unlock_iothread(); + goto out; + } + vm_stop_force_state(RUN_STATE_COLO); + qemu_mutex_unlock_iothread(); + trace_colo_vm_state_change("run", "stop"); + /* + * Failover request bh could be called after vm_stop_force_state(), + * So we need check failover_request_is_active() again. + */ + if (failover_get_state() != FAILOVER_STATUS_NONE) { + goto out; + } + + /* Disable block migration */ + s->params.blk = 0; + s->params.shared = 0; + qemu_savevm_state_header(fb); + qemu_savevm_state_begin(fb, &s->params); + qemu_mutex_lock_iothread(); + qemu_savevm_state_complete_precopy(fb, false); + qemu_mutex_unlock_iothread(); + + qemu_fflush(fb); + + colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err); + if (local_err) { + goto out; + } + /* + * We need the size of the VMstate data in Secondary side, + * With which we can decide how much data should be read. + */ + colo_send_message_value(s->to_dst_file, COLO_MESSAGE_VMSTATE_SIZE, + bioc->usage, &local_err); + if (local_err) { + goto out; + } + + qemu_put_buffer(s->to_dst_file, bioc->data, bioc->usage); + qemu_fflush(s->to_dst_file); + ret = qemu_file_get_error(s->to_dst_file); + if (ret < 0) { + goto out; + } + + colo_receive_check_message(s->rp_state.from_dst_file, + COLO_MESSAGE_VMSTATE_RECEIVED, &local_err); + if (local_err) { + goto out; + } + + colo_receive_check_message(s->rp_state.from_dst_file, + COLO_MESSAGE_VMSTATE_LOADED, &local_err); + if (local_err) { + goto out; + } + + ret = 0; + + qemu_mutex_lock_iothread(); + vm_start(); + qemu_mutex_unlock_iothread(); + trace_colo_vm_state_change("stop", "run"); + +out: + if (local_err) { + error_report_err(local_err); + } + return ret; +} + +static void colo_process_checkpoint(MigrationState *s) +{ + QIOChannelBuffer *bioc; + QEMUFile *fb = NULL; + int64_t current_time, checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST); + Error *local_err = NULL; + int ret; + + failover_init_state(); + + s->rp_state.from_dst_file = qemu_file_get_return_path(s->to_dst_file); + if (!s->rp_state.from_dst_file) { + error_report("Open QEMUFile from_dst_file failed"); + goto out; + } + + /* + * Wait for Secondary finish loading VM states and enter COLO + * restore. + */ + colo_receive_check_message(s->rp_state.from_dst_file, + COLO_MESSAGE_CHECKPOINT_READY, &local_err); + if (local_err) { + goto out; + } + bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); + fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc)); + object_unref(OBJECT(bioc)); + + qemu_mutex_lock_iothread(); + vm_start(); + qemu_mutex_unlock_iothread(); + trace_colo_vm_state_change("stop", "run"); + + while (s->state == MIGRATION_STATUS_COLO) { + if (failover_get_state() != FAILOVER_STATUS_NONE) { + error_report("failover request"); + goto out; + } + + current_time = qemu_clock_get_ms(QEMU_CLOCK_HOST); + if (current_time - checkpoint_time < + s->parameters.x_checkpoint_delay) { + int64_t delay_ms; + + delay_ms = s->parameters.x_checkpoint_delay - + (current_time - checkpoint_time); + g_usleep(delay_ms * 1000); + } + ret = colo_do_checkpoint_transaction(s, bioc, fb); + if (ret < 0) { + goto out; + } + checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST); + } + +out: + /* Throw the unreported error message after exited from loop */ + if (local_err) { + error_report_err(local_err); + } + + if (fb) { + qemu_fclose(fb); + } + + if (s->rp_state.from_dst_file) { + qemu_fclose(s->rp_state.from_dst_file); + } +} + +void migrate_start_colo_process(MigrationState *s) +{ + qemu_mutex_unlock_iothread(); + migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_COLO); + colo_process_checkpoint(s); + qemu_mutex_lock_iothread(); +} + +static void colo_wait_handle_message(QEMUFile *f, int *checkpoint_request, + Error **errp) +{ + COLOMessage msg; + Error *local_err = NULL; + + msg = colo_receive_message(f, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + switch (msg) { + case COLO_MESSAGE_CHECKPOINT_REQUEST: + *checkpoint_request = 1; + break; + default: + *checkpoint_request = 0; + error_setg(errp, "Got unknown COLO message: %d", msg); + break; + } +} + +void *colo_process_incoming_thread(void *opaque) +{ + MigrationIncomingState *mis = opaque; + QEMUFile *fb = NULL; + QIOChannelBuffer *bioc = NULL; /* Cache incoming device state */ + uint64_t total_size; + uint64_t value; + Error *local_err = NULL; + + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_COLO); + + failover_init_state(); + + mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); + if (!mis->to_src_file) { + error_report("COLO incoming thread: Open QEMUFile to_src_file failed"); + goto out; + } + /* + * Note: the communication between Primary side and Secondary side + * should be sequential, we set the fd to unblocked in migration incoming + * coroutine, and here we are in the COLO incoming thread, so it is ok to + * set the fd back to blocked. + */ + qemu_file_set_blocking(mis->from_src_file, true); + + bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); + fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); + object_unref(OBJECT(bioc)); + + colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_READY, + &local_err); + if (local_err) { + goto out; + } + + while (mis->state == MIGRATION_STATUS_COLO) { + int request; + + colo_wait_handle_message(mis->from_src_file, &request, &local_err); + if (local_err) { + goto out; + } + assert(request); + if (failover_get_state() != FAILOVER_STATUS_NONE) { + error_report("failover request"); + goto out; + } + + /* FIXME: This is unnecessary for periodic checkpoint mode */ + colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_REPLY, + &local_err); + if (local_err) { + goto out; + } + + colo_receive_check_message(mis->from_src_file, + COLO_MESSAGE_VMSTATE_SEND, &local_err); + if (local_err) { + goto out; + } + + value = colo_receive_message_value(mis->from_src_file, + COLO_MESSAGE_VMSTATE_SIZE, &local_err); + if (local_err) { + goto out; + } + + /* + * Read VM device state data into channel buffer, + * It's better to re-use the memory allocated. + * Here we need to handle the channel buffer directly. + */ + if (value > bioc->capacity) { + bioc->capacity = value; + bioc->data = g_realloc(bioc->data, bioc->capacity); + } + total_size = qemu_get_buffer(mis->from_src_file, bioc->data, value); + if (total_size != value) { + error_report("Got %" PRIu64 " VMState data, less than expected" + " %" PRIu64, total_size, value); + goto out; + } + bioc->usage = total_size; + qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); + + colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_RECEIVED, + &local_err); + if (local_err) { + goto out; + } + + qemu_mutex_lock_iothread(); + qemu_system_reset(VMRESET_SILENT); + if (qemu_loadvm_state(fb) < 0) { + error_report("COLO: loadvm failed"); + qemu_mutex_unlock_iothread(); + goto out; + } + qemu_mutex_unlock_iothread(); + + colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_LOADED, + &local_err); + if (local_err) { + goto out; + } + } + +out: + /* Throw the unreported error message after exited from loop */ + if (local_err) { + error_report_err(local_err); + } + + if (fb) { + qemu_fclose(fb); + } + + if (mis->to_src_file) { + qemu_fclose(mis->to_src_file); + } + migration_incoming_exit_colo(); + + return NULL; +} diff --git a/migration/exec.c b/migration/exec.c index 2af63cced6..9157721dfe 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -38,6 +38,7 @@ void exec_start_outgoing_migration(MigrationState *s, const char *command, Error return; } + qio_channel_set_name(ioc, "migration-exec-outgoing"); migration_channel_connect(s, ioc, NULL); object_unref(OBJECT(ioc)); } @@ -64,6 +65,7 @@ void exec_start_incoming_migration(const char *command, Error **errp) return; } + qio_channel_set_name(ioc, "migration-exec-incoming"); qio_channel_add_watch(ioc, G_IO_IN, exec_accept_incoming_migration, diff --git a/migration/fd.c b/migration/fd.c index 84a10fd68f..58cb51a9e6 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -38,6 +38,7 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** return; } + qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-outgoing"); migration_channel_connect(s, ioc, NULL); object_unref(OBJECT(ioc)); } @@ -65,6 +66,7 @@ void fd_start_incoming_migration(const char *infd, Error **errp) return; } + qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-incoming"); qio_channel_add_watch(ioc, G_IO_IN, fd_accept_incoming_migration, diff --git a/migration/migration.c b/migration/migration.c index 4d417b76cf..e331f28382 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -36,6 +36,7 @@ #include "exec/address-spaces.h" #include "io/channel-buffer.h" #include "io/channel-tls.h" +#include "migration/colo.h" #define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */ @@ -62,6 +63,11 @@ /* Migration XBZRLE default cache size */ #define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024) +/* The delay time (in ms) between two COLO checkpoints + * Note: Please change this default value to 10000 when we support hybrid mode. + */ +#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY 200 + static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -94,6 +100,7 @@ MigrationState *migrate_get_current(void) .cpu_throttle_increment = DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT, .max_bandwidth = MAX_THROTTLE, .downtime_limit = DEFAULT_MIGRATE_SET_DOWNTIME, + .x_checkpoint_delay = DEFAULT_MIGRATE_X_CHECKPOINT_DELAY, }, }; @@ -406,6 +413,18 @@ static void process_incoming_migration_co(void *opaque) /* Else if something went wrong then just fall out of the normal exit */ } + /* we get COLO info, and know if we are in COLO mode */ + if (!ret && migration_incoming_enable_colo()) { + mis->migration_incoming_co = qemu_coroutine_self(); + qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming", + colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); + mis->have_colo_incoming_thread = true; + qemu_coroutine_yield(); + + /* Wait checkpoint incoming thread exit before free resource */ + qemu_thread_join(&mis->colo_incoming_thread); + } + qemu_fclose(f); free_xbzrle_decoded_buf(); @@ -531,6 +550,9 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) caps = NULL; /* silence compiler warning */ for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (i == MIGRATION_CAPABILITY_X_COLO && !colo_supported()) { + continue; + } if (head == NULL) { head = g_malloc0(sizeof(*caps)); caps = head; @@ -571,6 +593,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->max_bandwidth = s->parameters.max_bandwidth; params->has_downtime_limit = true; params->downtime_limit = s->parameters.downtime_limit; + params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; return params; } @@ -691,6 +714,10 @@ MigrationInfo *qmp_query_migrate(Error **errp) get_xbzrle_cache_stats(info); break; + case MIGRATION_STATUS_COLO: + info->has_status = true; + /* TODO: display COLO specific information (checkpoint info etc.) */ + break; case MIGRATION_STATUS_COMPLETED: get_xbzrle_cache_stats(info); @@ -733,6 +760,14 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, } for (cap = params; cap; cap = cap->next) { + if (cap->value->capability == MIGRATION_CAPABILITY_X_COLO) { + if (!colo_supported()) { + error_setg(errp, "COLO is not currently supported, please" + " configure with --enable-colo option in order to" + " support COLO feature"); + continue; + } + } s->enabled_capabilities[cap->value->capability] = cap->value->state; } @@ -817,6 +852,11 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) "an integer in the range of 0 to 2000000 milliseconds"); return; } + if (params->has_x_checkpoint_delay && (params->x_checkpoint_delay < 0)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "x_checkpoint_delay", + "is invalid, it should be positive"); + } if (params->has_compress_level) { s->parameters.compress_level = params->compress_level; @@ -851,6 +891,10 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) if (params->has_downtime_limit) { s->parameters.downtime_limit = params->downtime_limit; } + + if (params->has_x_checkpoint_delay) { + s->parameters.x_checkpoint_delay = params->x_checkpoint_delay; + } } @@ -922,7 +966,7 @@ static void migrate_fd_cleanup(void *opaque) void migrate_fd_error(MigrationState *s, const Error *error) { - trace_migrate_fd_error(error ? error_get_pretty(error) : ""); + trace_migrate_fd_error(error_get_pretty(error)); assert(s->to_dst_file == NULL); migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); @@ -1101,7 +1145,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, params.shared = has_inc && inc; if (migration_is_setup_or_active(s->state) || - s->state == MIGRATION_STATUS_CANCELLING) { + s->state == MIGRATION_STATUS_CANCELLING || + s->state == MIGRATION_STATUS_COLO) { error_setg(errp, QERR_MIGRATION_ACTIVE); return; } @@ -1567,6 +1612,7 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running) * to do this we use a qemu_buf to hold the whole of the device state. */ bioc = qio_channel_buffer_new(4096); + qio_channel_set_name(QIO_CHANNEL(bioc), "migration-postcopy-buffer"); fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); @@ -1648,7 +1694,11 @@ static void migration_completion(MigrationState *s, int current_active_state, if (!ret) { ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - if (ret >= 0) { + /* + * Don't mark the image with BDRV_O_INACTIVE flag if + * we will go into COLO stage later. + */ + if (ret >= 0 && !migrate_colo_enabled()) { ret = bdrv_inactivate_all(); } if (ret >= 0) { @@ -1690,8 +1740,11 @@ static void migration_completion(MigrationState *s, int current_active_state, goto fail_invalidate; } - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_COMPLETED); + if (!migrate_colo_enabled()) { + migrate_set_state(&s->state, current_active_state, + MIGRATION_STATUS_COMPLETED); + } + return; fail_invalidate: @@ -1712,6 +1765,12 @@ fail: MIGRATION_STATUS_FAILED); } +bool migrate_colo_enabled(void) +{ + MigrationState *s = migrate_get_current(); + return s->enabled_capabilities[MIGRATION_CAPABILITY_X_COLO]; +} + /* * Master migration thread on the source VM. * It drives the migration and pumps the data down the outgoing channel. @@ -1730,6 +1789,7 @@ static void *migration_thread(void *opaque) bool entered_postcopy = false; /* The active state we expect to be in; ACTIVE or POSTCOPY_ACTIVE */ enum MigrationStatus current_active_state = MIGRATION_STATUS_ACTIVE; + bool enable_colo = migrate_colo_enabled(); rcu_register_thread(); @@ -1838,7 +1898,13 @@ static void *migration_thread(void *opaque) end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); qemu_mutex_lock_iothread(); - qemu_savevm_state_cleanup(); + /* + * The resource has been allocated by migration will be reused in COLO + * process, so don't release them. + */ + if (!enable_colo) { + qemu_savevm_state_cleanup(); + } if (s->state == MIGRATION_STATUS_COMPLETED) { uint64_t transferred_bytes = qemu_ftell(s->to_dst_file); s->total_time = end_time - s->total_time; @@ -1851,6 +1917,15 @@ static void *migration_thread(void *opaque) } runstate_set(RUN_STATE_POSTMIGRATE); } else { + if (s->state == MIGRATION_STATUS_ACTIVE && enable_colo) { + migrate_start_colo_process(s); + qemu_savevm_state_cleanup(); + /* + * Fixme: we will run VM in COLO no matter its old running state. + * After exited COLO, we will keep running. + */ + old_vm_running = true; + } if (old_vm_running && !entered_postcopy) { vm_start(); } else { diff --git a/migration/ram.c b/migration/ram.c index d032d389c4..fb9252d722 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -43,6 +43,7 @@ #include "trace.h" #include "exec/ram_addr.h" #include "qemu/rcu_queue.h" +#include "migration/colo.h" #ifdef DEBUG_MIGRATION_RAM #define DPRINTF(fmt, ...) \ @@ -1871,16 +1872,8 @@ err: return ret; } - -/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has - * long-running RCU critical section. When rcu-reclaims in the code - * start to become numerous it will be necessary to reduce the - * granularity of these critical sections. - */ - -static int ram_save_setup(QEMUFile *f, void *opaque) +static int ram_save_init_globals(void) { - RAMBlock *block; int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */ dirty_rate_high_cnt = 0; @@ -1947,6 +1940,29 @@ static int ram_save_setup(QEMUFile *f, void *opaque) migration_bitmap_sync(); qemu_mutex_unlock_ramlist(); qemu_mutex_unlock_iothread(); + rcu_read_unlock(); + + return 0; +} + +/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has + * long-running RCU critical section. When rcu-reclaims in the code + * start to become numerous it will be necessary to reduce the + * granularity of these critical sections. + */ + +static int ram_save_setup(QEMUFile *f, void *opaque) +{ + RAMBlock *block; + + /* migration has already setup the bitmap, reuse it. */ + if (!migration_in_colo_state()) { + if (ram_save_init_globals() < 0) { + return -1; + } + } + + rcu_read_lock(); qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); @@ -2048,7 +2064,8 @@ static int ram_save_complete(QEMUFile *f, void *opaque) while (true) { int pages; - pages = ram_find_and_save_block(f, true, &bytes_transferred); + pages = ram_find_and_save_block(f, !migration_in_colo_state(), + &bytes_transferred); /* no more blocks to sent */ if (pages == 0) { break; diff --git a/migration/savevm.c b/migration/savevm.c index cfcbbd00d3..0363372acc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1631,6 +1631,7 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) } bioc = qio_channel_buffer_new(length); + qio_channel_set_name(QIO_CHANNEL(bioc), "migration-loadvm-buffer"); ret = qemu_get_buffer(mis->from_src_file, bioc->data, length); @@ -2122,6 +2123,7 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp) if (!ioc) { goto the_end; } + qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-save-state"); f = qemu_fopen_channel_output(QIO_CHANNEL(ioc)); ret = qemu_save_device_state(f); qemu_fclose(f); @@ -2154,6 +2156,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) if (!ioc) { return; } + qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state"); f = qemu_fopen_channel_input(QIO_CHANNEL(ioc)); migration_incoming_state_new(f); diff --git a/migration/socket.c b/migration/socket.c index a21c0c5c35..11f80b119b 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -100,6 +100,7 @@ static void socket_start_outgoing_migration(MigrationState *s, data->hostname = g_strdup(saddr->u.inet.data->host); } + qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-outgoing"); qio_channel_socket_connect_async(sioc, saddr, socket_outgoing_migration, @@ -146,6 +147,7 @@ static gboolean socket_accept_incoming_migration(QIOChannel *ioc, trace_migration_socket_incoming_accepted(); + qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-incoming"); migration_channel_process_incoming(migrate_get_current(), QIO_CHANNEL(sioc)); object_unref(OBJECT(sioc)); @@ -162,6 +164,9 @@ static void socket_start_incoming_migration(SocketAddress *saddr, { QIOChannelSocket *listen_ioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(listen_ioc), + "migration-socket-listener"); + if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) { object_unref(OBJECT(listen_ioc)); qapi_free_SocketAddress(saddr); diff --git a/migration/tls.c b/migration/tls.c index 12c053d15f..49ca9a8930 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -99,6 +99,7 @@ void migration_tls_channel_process_incoming(MigrationState *s, } trace_migration_tls_incoming_handshake_start(); + qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-incoming"); qio_channel_tls_handshake(tioc, migration_tls_incoming_handshake, NULL, @@ -154,6 +155,7 @@ void migration_tls_channel_connect(MigrationState *s, } trace_migration_tls_outgoing_handshake_start(hostname); + qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-outgoing"); qio_channel_tls_handshake(tioc, migration_tls_outgoing_handshake, s, diff --git a/migration/trace-events b/migration/trace-events index dfee75abf4..94134f700b 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -207,3 +207,9 @@ migration_tls_outgoing_handshake_complete(void) "" migration_tls_incoming_handshake_start(void) "" migration_tls_incoming_handshake_error(const char *err) "err=%s" migration_tls_incoming_handshake_complete(void) "" + +# migration/colo.c +colo_vm_state_change(const char *old, const char *new) "Change '%s' => '%s'" +colo_send_message(const char *msg) "Send '%s' message" +colo_receive_message(const char *msg) "Receive '%s' message" +colo_failover_set_state(const char *new_state) "new state %s" @@ -59,7 +59,6 @@ #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include "qom/object_interfaces.h" -#include "cpu.h" #include "trace.h" #include "trace/control.h" #include "monitor/hmp-target.h" @@ -76,7 +75,6 @@ #include "qapi/qmp-event.h" #include "qapi-event.h" #include "qmp-introspect.h" -#include "sysemu/block-backend.h" #include "sysemu/qtest.h" #include "qemu/cutils.h" #include "qapi/qmp/dispatch.h" @@ -4094,7 +4092,7 @@ QemuOptsList qemu_mon_opts = { .name = "chardev", .type = QEMU_OPT_STRING, },{ - .name = "default", + .name = "default", /* deprecated */ .type = QEMU_OPT_BOOL, },{ .name = "pretty", diff --git a/nbd/client.c b/nbd/client.c index a92f1e2275..f6db8369b3 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -387,6 +387,7 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, if (!tioc) { return NULL; } + qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); data.loop = g_main_loop_new(g_main_context_default(), FALSE); TRACE("Starting TLS handshake"); qio_channel_tls_handshake(tioc, diff --git a/nbd/server.c b/nbd/server.c index 472f584c32..36bcafcd50 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -349,6 +349,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, return NULL; } + qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls"); TRACE("Starting TLS handshake"); data.loop = g_main_loop_new(g_main_context_default(), FALSE); qio_channel_tls_handshake(tioc, diff --git a/net/colo-compare.c b/net/colo-compare.c index 109990fd8e..9bfc736f55 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -92,10 +92,6 @@ typedef struct CompareClass { ObjectClass parent_class; } CompareClass; -typedef struct CompareChardevProps { - bool is_socket; -} CompareChardevProps; - enum { PRIMARY_IN = 0, SECONDARY_IN, @@ -188,7 +184,6 @@ static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt) { struct tcphdr *ptcp, *stcp; int res; - char *sdebug, *ddebug; trace_colo_compare_main("compare tcp"); if (ppkt->size != spkt->size) { @@ -219,24 +214,22 @@ static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt) (spkt->size - ETH_HLEN)); if (res != 0 && trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE)) { - sdebug = strdup(inet_ntoa(ppkt->ip->ip_src)); - ddebug = strdup(inet_ntoa(ppkt->ip->ip_dst)); - fprintf(stderr, "%s: src/dst: %s/%s p: seq/ack=%u/%u" - " s: seq/ack=%u/%u res=%d flags=%x/%x\n", - __func__, sdebug, ddebug, - (unsigned int)ntohl(ptcp->th_seq), - (unsigned int)ntohl(ptcp->th_ack), - (unsigned int)ntohl(stcp->th_seq), - (unsigned int)ntohl(stcp->th_ack), - res, ptcp->th_flags, stcp->th_flags); - - fprintf(stderr, "Primary len = %d\n", ppkt->size); - qemu_hexdump((char *)ppkt->data, stderr, "colo-compare", ppkt->size); - fprintf(stderr, "Secondary len = %d\n", spkt->size); - qemu_hexdump((char *)spkt->data, stderr, "colo-compare", spkt->size); - - g_free(sdebug); - g_free(ddebug); + trace_colo_compare_pkt_info_src(inet_ntoa(ppkt->ip->ip_src), + ntohl(stcp->th_seq), + ntohl(stcp->th_ack), + res, stcp->th_flags, + spkt->size); + + trace_colo_compare_pkt_info_dst(inet_ntoa(ppkt->ip->ip_dst), + ntohl(ptcp->th_seq), + ntohl(ptcp->th_ack), + res, ptcp->th_flags, + ppkt->size); + + qemu_hexdump((char *)ppkt->data, stderr, + "colo-compare ppkt", ppkt->size); + qemu_hexdump((char *)spkt->data, stderr, + "colo-compare spkt", spkt->size); } return res; @@ -575,8 +568,6 @@ static int find_and_check_chardev(CharDriverState **chr, char *chr_name, Error **errp) { - CompareChardevProps props; - *chr = qemu_chr_find(chr_name); if (*chr == NULL) { error_setg(errp, "Device '%s' not found", @@ -584,8 +575,6 @@ static int find_and_check_chardev(CharDriverState **chr, return 1; } - memset(&props, 0, sizeof(props)); - if (!qemu_chr_has_feature(*chr, QEMU_CHAR_FEATURE_RECONNECTABLE)) { error_setg(errp, "chardev \"%s\" is not reconnectable", chr_name); diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index 89abe72d4e..c4ab91cdee 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -68,15 +68,11 @@ static int handle_primary_tcp_pkt(NetFilterState *nf, tcp_pkt = (struct tcphdr *)pkt->transport_header; if (trace_event_get_state(TRACE_COLO_FILTER_REWRITER_DEBUG)) { - char *sdebug, *ddebug; - sdebug = strdup(inet_ntoa(pkt->ip->ip_src)); - ddebug = strdup(inet_ntoa(pkt->ip->ip_dst)); - trace_colo_filter_rewriter_pkt_info(__func__, sdebug, ddebug, + trace_colo_filter_rewriter_pkt_info(__func__, + inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), tcp_pkt->th_flags); trace_colo_filter_rewriter_conn_offset(conn->offset); - g_free(sdebug); - g_free(ddebug); } if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { @@ -116,15 +112,11 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf, tcp_pkt = (struct tcphdr *)pkt->transport_header; if (trace_event_get_state(TRACE_COLO_FILTER_REWRITER_DEBUG)) { - char *sdebug, *ddebug; - sdebug = strdup(inet_ntoa(pkt->ip->ip_src)); - ddebug = strdup(inet_ntoa(pkt->ip->ip_dst)); - trace_colo_filter_rewriter_pkt_info(__func__, sdebug, ddebug, + trace_colo_filter_rewriter_pkt_info(__func__, + inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), tcp_pkt->th_flags); trace_colo_filter_rewriter_conn_offset(conn->offset); - g_free(sdebug); - g_free(ddebug); } if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { @@ -162,6 +154,7 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, iov_to_buf(iov, iovcnt, 0, buf, size); pkt = packet_new(buf, size); + g_free(buf); /* * if we get tcp packet diff --git a/net/tap-bsd.c b/net/tap-bsd.c index c506ac31d6..6c9692263d 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -35,6 +35,10 @@ #include <net/if_tap.h> #endif +#if defined(__OpenBSD__) +#include <sys/param.h> +#endif + #ifndef __FreeBSD__ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required, int mq_required, Error **errp) @@ -55,7 +59,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, if (*ifname) { snprintf(dname, sizeof dname, "/dev/%s", ifname); } else { -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) && OpenBSD < 201605 snprintf(dname, sizeof dname, "/dev/tun%d", i); #else snprintf(dname, sizeof dname, "/dev/tap%d", i); diff --git a/net/trace-events b/net/trace-events index d67f048825..35198bc742 100644 --- a/net/trace-events +++ b/net/trace-events @@ -13,6 +13,8 @@ colo_compare_icmp_miscompare(const char *sta, int size) ": %s = %d" colo_compare_ip_info(int psize, const char *sta, const char *stb, int ssize, const char *stc, const char *std) "ppkt size = %d, ip_src = %s, ip_dst = %s, spkt size = %d, ip_src = %s, ip_dst = %s" colo_old_packet_check_found(int64_t old_time) "%" PRId64 colo_compare_miscompare(void) "" +colo_compare_pkt_info_src(const char *src, uint32_t sseq, uint32_t sack, int res, uint32_t sflag, int ssize) "src/dst: %s s: seq/ack=%u/%u res=%d flags=%x spkt_size: %d\n" +colo_compare_pkt_info_dst(const char *dst, uint32_t dseq, uint32_t dack, int res, uint32_t dflag, int dsize) "src/dst: %s d: seq/ack=%u/%u res=%d flags=%x dpkt_size: %d\n" # net/filter-rewriter.c colo_filter_rewriter_debug(void) "" diff --git a/pc-bios/README b/pc-bios/README index 5a8a93193e..47a913f9c7 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -17,7 +17,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20160223. + built from git tag qemu-slof-20161019. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as @@ -42,3 +42,8 @@ it was compiled using the qemu-ppce500 target. A git mirror is available at: git://git.qemu-project.org/u-boot.git The hash used to compile the current version is: 2072e72 + +- Skiboot (https://github.com/open-power/skiboot/) is an OPAL + (OpenPower Abstraction Layer) firmware for OpenPOWER systems. It can + run an hypervisor OS or simply a host OS on the "baremetal" + platform, also known as the PowerNV (Non-Virtualized) platform. diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin Binary files differindex 57fb4d88b1..8dc4838a9b 100644 --- a/pc-bios/bios-256k.bin +++ b/pc-bios/bios-256k.bin diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex 8a6869ff1b..0b16c546ec 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid Binary files differnew file mode 100644 index 0000000000..0e59a8280d --- /dev/null +++ b/pc-bios/skiboot.lid diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin Binary files differindex f2384939ef..30ce7ac384 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin Binary files differindex 3f4bb30fe3..67389ca75b 100644 --- a/pc-bios/vgabios-cirrus.bin +++ b/pc-bios/vgabios-cirrus.bin diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin Binary files differindex 38d31b6b5b..9d3d9b4031 100644 --- a/pc-bios/vgabios-qxl.bin +++ b/pc-bios/vgabios-qxl.bin diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin Binary files differindex e469c107a7..6fc42b1289 100644 --- a/pc-bios/vgabios-stdvga.bin +++ b/pc-bios/vgabios-stdvga.bin diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin Binary files differindex d42b02873a..b2e6e5a235 100644 --- a/pc-bios/vgabios-virtio.bin +++ b/pc-bios/vgabios-virtio.bin diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin Binary files differindex 26bc0b704c..eccd87b458 100644 --- a/pc-bios/vgabios-vmware.bin +++ b/pc-bios/vgabios-vmware.bin diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin Binary files differindex 2d1a7c6c95..450230ab42 100644 --- a/pc-bios/vgabios.bin +++ b/pc-bios/vgabios.bin diff --git a/qapi-schema.json b/qapi-schema.json index d6a43a108c..8a7b527091 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -175,12 +175,15 @@ # @watchdog: the watchdog action is configured to pause and has been triggered # # @guest-panicked: guest has been panicked as a result of guest OS panic +# +# @colo: guest is paused to save/restore VM state under colo checkpoint (since +# 2.8) ## { 'enum': 'RunState', 'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused', 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm', 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog', - 'guest-panicked' ] } + 'guest-panicked', 'colo' ] } ## # @StatusInfo: @@ -459,12 +462,14 @@ # # @failed: some error occurred during migration process. # +# @colo: VM is in the process of fault tolerance. (since 2.8) +# # Since: 2.3 # ## { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', - 'active', 'postcopy-active', 'completed', 'failed' ] } + 'active', 'postcopy-active', 'completed', 'failed', 'colo' ] } ## # @MigrationInfo @@ -574,11 +579,16 @@ # been migrated, pulling the remaining pages along as needed. NOTE: If # the migration fails during postcopy the VM will fail. (since 2.6) # +# @x-colo: If enabled, migration will never end, and the state of the VM on the +# primary side will be migrated continuously to the VM on secondary +# side, this process is called COarse-Grain LOck Stepping (COLO) for +# Non-stop Service. (since 2.8) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - 'compress', 'events', 'postcopy-ram'] } + 'compress', 'events', 'postcopy-ram', 'x-colo'] } ## # @MigrationCapabilityStatus @@ -664,19 +674,24 @@ # @downtime-limit: set maximum tolerated downtime for migration. maximum # downtime in milliseconds (Since 2.8) # +# @x-checkpoint-delay: The delay time (in ms) between two COLO checkpoints in +# periodic mode. (Since 2.8) +# # Since: 2.4 ## { 'enum': 'MigrationParameter', 'data': ['compress-level', 'compress-threads', 'decompress-threads', 'cpu-throttle-initial', 'cpu-throttle-increment', 'tls-creds', 'tls-hostname', 'max-bandwidth', - 'downtime-limit'] } + 'downtime-limit', 'x-checkpoint-delay' ] } # # @migrate-set-parameters # # Set various migration parameters. See MigrationParameters for details. # +# @x-checkpoint-delay: the delay time between two checkpoints. (Since 2.8) +# # Since: 2.4 ## { 'command': 'migrate-set-parameters', 'boxed': true, @@ -725,6 +740,8 @@ # @downtime-limit: set maximum tolerated downtime for migration. maximum # downtime in milliseconds (Since 2.8) # +# @x-checkpoint-delay: the delay time between two COLO checkpoints. (Since 2.8) +# # Since: 2.4 ## { 'struct': 'MigrationParameters', @@ -736,7 +753,8 @@ '*tls-creds': 'str', '*tls-hostname': 'str', '*max-bandwidth': 'int', - '*downtime-limit': 'int'} } + '*downtime-limit': 'int', + '*x-checkpoint-delay': 'int'} } ## # @query-migrate-parameters @@ -780,6 +798,78 @@ { 'command': 'migrate-start-postcopy' } ## +# @COLOMessage +# +# The message transmission between Primary side and Secondary side. +# +# @checkpoint-ready: Secondary VM (SVM) is ready for checkpointing +# +# @checkpoint-request: Primary VM (PVM) tells SVM to prepare for checkpointing +# +# @checkpoint-reply: SVM gets PVM's checkpoint request +# +# @vmstate-send: VM's state will be sent by PVM. +# +# @vmstate-size: The total size of VMstate. +# +# @vmstate-received: VM's state has been received by SVM. +# +# @vmstate-loaded: VM's state has been loaded by SVM. +# +# Since: 2.8 +## +{ 'enum': 'COLOMessage', + 'data': [ 'checkpoint-ready', 'checkpoint-request', 'checkpoint-reply', + 'vmstate-send', 'vmstate-size', 'vmstate-received', + 'vmstate-loaded' ] } + +## +# @COLOMode +# +# The colo mode +# +# @unknown: unknown mode +# +# @primary: master side +# +# @secondary: slave side +# +# Since: 2.8 +## +{ 'enum': 'COLOMode', + 'data': [ 'unknown', 'primary', 'secondary'] } + +## +# @FailoverStatus +# +# An enumeration of COLO failover status +# +# @none: no failover has ever happened +# +# @require: got failover requirement but not handled +# +# @active: in the process of doing failover +# +# @completed: finish the process of failover +# +# Since: 2.8 +## +{ 'enum': 'FailoverStatus', + 'data': [ 'none', 'require', 'active', 'completed'] } + +## +# @x-colo-lost-heartbeat +# +# Tell qemu that heartbeat is lost, request it to do takeover procedures. +# If this command is sent to the PVM, the Primary side will exit COLO mode. +# If sent to the Secondary, the Secondary side will run failover work, +# then takes over server operation to become the service VM. +# +# Since: 2.8 +## +{ 'command': 'x-colo-lost-heartbeat' } + +## # @MouseInfo: # # Information about a mouse device. diff --git a/qapi/block-core.json b/qapi/block-core.json index 97b120532a..cd1fa7ba07 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1703,15 +1703,16 @@ # # @host_device, @host_cdrom: Since 2.1 # @gluster: Since 2.7 +# @nbd: Since 2.8 # # Since: 2.0 ## { 'enum': 'BlockdevDriver', 'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop', 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', - 'host_device', 'http', 'https', 'luks', 'null-aio', 'null-co', - 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', - 'replication', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } + 'host_device', 'http', 'https', 'luks', 'nbd', 'null-aio', + 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', + 'replication', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } ## # @BlockdevOptionsFile @@ -2220,6 +2221,24 @@ 'data': { 'filename': 'str' } } ## +# @BlockdevOptionsNbd +# +# Driver specific block device options for NBD. +# +# @server: NBD server address +# +# @export: #optional export name +# +# @tls-creds: #optional TLS credentials ID +# +# Since: 2.8 +## +{ 'struct': 'BlockdevOptionsNbd', + 'data': { 'server': 'SocketAddress', + '*export': 'str', + '*tls-creds': 'str' } } + +## # @BlockdevOptions # # Options for creating a block device. Many options are available for all @@ -2264,7 +2283,7 @@ 'https': 'BlockdevOptionsCurl', # TODO iscsi: Wait for structured options 'luks': 'BlockdevOptionsLUKS', -# TODO nbd: Should take InetSocketAddress for 'host'? + 'nbd': 'BlockdevOptionsNbd', # TODO nfs: Wait for structured options 'null-aio': 'BlockdevOptionsNull', 'null-co': 'BlockdevOptionsNull', diff --git a/qemu-char.c b/qemu-char.c index 6dd779fc3c..1e5a0e8cb9 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1075,7 +1075,8 @@ static GSourceFuncs io_watch_poll_funcs = { }; /* Can only be used for read */ -static guint io_add_watch_poll(QIOChannel *ioc, +static guint io_add_watch_poll(CharDriverState *chr, + QIOChannel *ioc, IOCanReadHandler *fd_can_read, QIOChannelFunc fd_read, gpointer user_data, @@ -1083,6 +1084,7 @@ static guint io_add_watch_poll(QIOChannel *ioc, { IOWatchPoll *iwp; int tag; + char *name; iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll)); @@ -1093,6 +1095,10 @@ static guint io_add_watch_poll(QIOChannel *ioc, iwp->src = NULL; iwp->context = context; + name = g_strdup_printf("chardev-iowatch-%s", chr->label); + g_source_set_name((GSource *)iwp, name); + g_free(name); + tag = g_source_attach(&iwp->parent, context); g_source_unref(&iwp->parent); return tag; @@ -1232,7 +1238,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr, remove_fd_in_watch(chr); if (s->ioc_in) { - chr->fd_in_tag = io_add_watch_poll(s->ioc_in, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in, fd_chr_read_poll, fd_chr_read, chr, context); @@ -1261,6 +1267,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, { CharDriverState *chr; FDCharDriver *s; + char *name; chr = qemu_chr_alloc(backend, errp); if (!chr) { @@ -1268,7 +1275,13 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, } s = g_new0(FDCharDriver, 1); s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in)); + name = g_strdup_printf("chardev-file-in-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name); + g_free(name); s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out)); + name = g_strdup_printf("chardev-file-out-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name); + g_free(name); qemu_set_nonblock(fd_out); s->chr = chr; chr->opaque = s; @@ -1448,6 +1461,7 @@ static gboolean pty_chr_timer(gpointer opaque) static void pty_chr_rearm_timer(CharDriverState *chr, int ms) { PtyCharDriver *s = chr->opaque; + char *name; if (s->timer_tag) { g_source_remove(s->timer_tag); @@ -1455,10 +1469,14 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms) } if (ms == 1000) { + name = g_strdup_printf("pty-timer-secs-%s", chr->label); s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr); } else { + name = g_strdup_printf("pty-timer-ms-%s", chr->label); s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr); } + g_source_set_name_by_id(s->timer_tag, name); + g_free(name); } /* Called with chr_write_lock held. */ @@ -1587,7 +1605,7 @@ static void pty_chr_state(CharDriverState *chr, int connected) s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr); } if (!chr->fd_in_tag) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, pty_chr_read_poll, pty_chr_read, chr, NULL); @@ -1622,6 +1640,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, int master_fd, slave_fd; char pty_name[PATH_MAX]; ChardevCommon *common = backend->u.pty.data; + char *name; master_fd = qemu_openpty_raw(&slave_fd, pty_name); if (master_fd < 0) { @@ -1654,6 +1673,9 @@ static CharDriverState *qemu_chr_open_pty(const char *id, *be_opened = false; s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); + name = g_strdup_printf("chardev-pty-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(s->ioc), name); + g_free(name); s->timer_tag = 0; return chr; @@ -2744,7 +2766,7 @@ static void udp_chr_update_read_handler(CharDriverState *chr, remove_fd_in_watch(chr); if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, udp_chr_read_poll, udp_chr_read, chr, context); @@ -2822,9 +2844,13 @@ static gboolean socket_reconnect_timeout(gpointer opaque); static void qemu_chr_socket_restart_timer(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + char *name; assert(s->connected == 0); s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time, socket_reconnect_timeout, chr); + name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label); + g_source_set_name_by_id(s->reconnect_timer, name); + g_free(name); } static void check_report_connect_error(CharDriverState *chr, @@ -3149,7 +3175,7 @@ static void tcp_chr_connect(void *opaque) s->connected = 1; if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, tcp_chr_read_poll, tcp_chr_read, chr, NULL); @@ -3168,7 +3194,7 @@ static void tcp_chr_update_read_handler(CharDriverState *chr, remove_fd_in_watch(chr); if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, tcp_chr_read_poll, tcp_chr_read, chr, context); @@ -3266,6 +3292,7 @@ static void tcp_chr_tls_init(CharDriverState *chr) TCPCharDriver *s = chr->opaque; QIOChannelTLS *tioc; Error *err = NULL; + gchar *name; if (s->is_listen) { tioc = qio_channel_tls_new_server( @@ -3283,6 +3310,11 @@ static void tcp_chr_tls_init(CharDriverState *chr) tcp_chr_disconnect(chr); return; } + name = g_strdup_printf("chardev-tls-%s-%s", + s->is_listen ? "server" : "client", + chr->label); + qio_channel_set_name(QIO_CHANNEL(tioc), name); + g_free(name); object_unref(OBJECT(s->ioc)); s->ioc = QIO_CHANNEL(tioc); @@ -3293,6 +3325,19 @@ static void tcp_chr_tls_init(CharDriverState *chr) } +static void tcp_chr_set_client_ioc_name(CharDriverState *chr, + QIOChannelSocket *sioc) +{ + TCPCharDriver *s = chr->opaque; + char *name; + name = g_strdup_printf("chardev-tcp-%s-%s", + s->is_listen ? "server" : "client", + chr->label); + qio_channel_set_name(QIO_CHANNEL(sioc), name); + g_free(name); + +} + static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc) { TCPCharDriver *s = chr->opaque; @@ -3338,6 +3383,7 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd) if (!sioc) { return -1; } + tcp_chr_set_client_ioc_name(chr, sioc); ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return ret; @@ -3379,6 +3425,7 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); } else { sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { object_unref(OBJECT(sioc)); return -1; @@ -4586,6 +4633,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque) } sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); qio_channel_socket_connect_async(sioc, s->addr, qemu_chr_socket_connected, chr, NULL); @@ -4688,12 +4736,19 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, if (s->reconnect_time) { sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); qio_channel_socket_connect_async(sioc, s->addr, qemu_chr_socket_connected, chr, NULL); } else { if (s->is_listen) { + char *name; sioc = qio_channel_socket_new(); + + name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(sioc), name); + g_free(name); + if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) { goto error; } @@ -4735,6 +4790,8 @@ static CharDriverState *qmp_chardev_open_udp(const char *id, ChardevUdp *udp = backend->u.udp.data; ChardevCommon *common = qapi_ChardevUdp_base(udp); QIOChannelSocket *sioc = qio_channel_socket_new(); + char *name; + CharDriverState *chr; if (qio_channel_socket_dgram_sync(sioc, udp->local, udp->remote, @@ -4742,7 +4799,13 @@ static CharDriverState *qmp_chardev_open_udp(const char *id, object_unref(OBJECT(sioc)); return NULL; } - return qemu_chr_open_udp(sioc, common, be_opened, errp); + chr = qemu_chr_open_udp(sioc, common, be_opened, errp); + + name = g_strdup_printf("chardev-udp-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(sioc), name); + g_free(name); + + return chr; } diff --git a/qemu-ga.texi b/qemu-ga.texi index 0e53bf6b2c..4c7a8fd163 100644 --- a/qemu-ga.texi +++ b/qemu-ga.texi @@ -30,7 +30,7 @@ set user's password @end itemize qemu-ga will read a system configuration file on startup (located at -q@file{/etc/qemu/qemu-ga.conf} by default), then parse remaining +@file{/etc/qemu/qemu-ga.conf} by default), then parse remaining configuration options on the command line. For the same key, the last option wins, but the lists accumulate (see below for configuration file format). diff --git a/qemu-img.c b/qemu-img.c index afcd51ff18..ac7f40d91a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -795,6 +795,7 @@ static void run_block_job(BlockJob *job, Error **errp) { AioContext *aio_context = blk_get_aio_context(job->blk); + aio_context_acquire(aio_context); do { aio_poll(aio_context, true); qemu_progress_print(job->len ? @@ -802,6 +803,7 @@ static void run_block_job(BlockJob *job, Error **errp) } while (!job->ready); block_job_complete_sync(job, errp); + aio_context_release(aio_context); /* A block job may finish instantaneously without publishing any progress, * so just signal completion here */ @@ -819,6 +821,7 @@ static int img_commit(int argc, char **argv) Error *local_err = NULL; CommonBlockJobCBInfo cbi; bool image_opts = false; + AioContext *aio_context; fmt = NULL; cache = BDRV_DEFAULT_CACHE; @@ -928,8 +931,11 @@ static int img_commit(int argc, char **argv) .bs = bs, }; + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); commit_active_start("commit", bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT, common_block_job_cb, &cbi, &local_err, false); + aio_context_release(aio_context); if (local_err) { goto done; } diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 3a3838a079..95bcde1d88 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -18,7 +18,6 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/timer.h" -#include "sysemu/block-backend.h" #include "qemu/cutils.h" #define CMD_NOFILE_OK 0x01 @@ -1956,7 +1955,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) qemu_opts_reset(&reopen_opts); brq = bdrv_reopen_queue(NULL, bs, opts, flags); - bdrv_reopen_multiple(brq, &local_err); + bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err); if (local_err) { error_report_err(local_err); } else { @@ -2216,6 +2215,7 @@ static const cmdinfo_t help_cmd = { bool qemuio_command(BlockBackend *blk, const char *cmd) { + AioContext *ctx; char *input; const cmdinfo_t *ct; char **v; @@ -2227,7 +2227,10 @@ bool qemuio_command(BlockBackend *blk, const char *cmd) if (c) { ct = find_command(v[0]); if (ct) { + ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); + aio_context_acquire(ctx); done = command(blk, ct, c, v); + aio_context_release(ctx); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); } diff --git a/qemu-options.hx b/qemu-options.hx index b1fbdb08cd..95332cc05b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -172,7 +172,7 @@ DEF("set", HAS_ARG, QEMU_OPTION_set, STEXI @item -set @var{group}.@var{id}.@var{arg}=@var{value} @findex -set -Set parameter @var{arg} for item @var{id} of type @var{group}\n" +Set parameter @var{arg} for item @var{id} of type @var{group} ETEXI DEF("global", HAS_ARG, QEMU_OPTION_global, @@ -2239,7 +2239,7 @@ two serial ports and the QEMU monitor: @example -chardev stdio,mux=on,id=char0 \ --mon chardev=char0,mode=readline,default \ +-mon chardev=char0,mode=readline \ -serial chardev:char0 \ -serial chardev:char0 @end example @@ -2250,7 +2250,7 @@ multiplexed between the QEMU monitor and a parallel port: @example -chardev stdio,mux=on,id=char0 \ --mon chardev=char0,mode=readline,default \ +-mon chardev=char0,mode=readline \ -parallel chardev:char0 \ -chardev tcp,...,mux=on,id=char1 \ -serial chardev:char1 \ @@ -3112,9 +3112,9 @@ Like -qmp but uses pretty JSON formatting. ETEXI DEF("mon", HAS_ARG, QEMU_OPTION_mon, \ - "-mon [chardev=]name[,mode=readline|control][,default]\n", QEMU_ARCH_ALL) + "-mon [chardev=]name[,mode=readline|control]\n", QEMU_ARCH_ALL) STEXI -@item -mon [chardev=]name[,mode=readline|control][,default] +@item -mon [chardev=]name[,mode=readline|control] @findex -mon Setup monitor on chardev @var{name}. ETEXI @@ -3902,7 +3902,7 @@ colo secondary: -object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 -object filter-rewriter,id=rew0,netdev=hn0,queue=all -@item -object filter-dump,id=@var{id},netdev=@var{dev},file=@var{filename}][,maxlen=@var{len}] +@item -object filter-dump,id=@var{id},netdev=@var{dev}[,file=@var{filename}][,maxlen=@var{len}] Dump the network traffic on netdev @var{dev} to the file specified by @var{filename}. At most @var{len} bytes (64k by default) per packet are stored. @@ -36,7 +36,6 @@ #include "qom/object_interfaces.h" #include "hw/mem/pc-dimm.h" #include "hw/acpi/acpi_dev_interface.h" -#include "qemu/uuid.h" NameInfo *qmp_query_name(Error **errp) { diff --git a/roms/Makefile b/roms/Makefile index 88b3709d4d..b5e5a69e91 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -63,6 +63,7 @@ default: @echo " efirom -- update nic roms (bios+efi, this needs" @echo " the EfiRom utility from edk2 / tianocore)" @echo " slof -- update slof.bin" + @echo " skiboot -- update skiboot.lid" @echo " u-boot.e500 -- update u-boot.e500" bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k @@ -103,7 +104,7 @@ build-lgplvgabios: $(MAKE) -C vgabios $(vgabios_targets) -.PHONY: sgabios +.PHONY: sgabios skiboot sgabios: $(MAKE) -C sgabios cp sgabios/sgabios.bin ../pc-bios @@ -146,6 +147,10 @@ u-boot.e500: $(powerpc_cross_prefix)strip u-boot/build.e500/u-boot -o \ ../pc-bios/u-boot.e500 +skiboot: + $(MAKE) -C skiboot CROSS=$(powerpc64_cross_prefix) + cp skiboot/skiboot.lid ../pc-bios/skiboot.lid + clean: rm -rf seabios/.config seabios/out seabios/builds $(MAKE) -C vgabios clean @@ -155,3 +160,4 @@ clean: $(MAKE) -C ipxe/src veryclean $(MAKE) -C SLOF clean rm -rf u-boot/build.e500 + $(MAKE) -C skiboot clean diff --git a/roms/SLOF b/roms/SLOF -Subproject e3d05727a074619fc12d0a67f05cf2c42c875cc +Subproject efd65f49929d7db775b26066d538c8120ae3db9 diff --git a/roms/seabios b/roms/seabios -Subproject e2fc41e24ee0ada60fc511d60b15a41b294538b +Subproject d7adf6044a4c772b497e97272adf97426b34a24 diff --git a/roms/skiboot b/roms/skiboot new file mode 160000 +Subproject 762d0082f18e4fb921a2d44a1051b02d8b0f638 diff --git a/scripts/clean-includes b/scripts/clean-includes index 4412a5590a..dd938daa3e 100755 --- a/scripts/clean-includes +++ b/scripts/clean-includes @@ -14,15 +14,18 @@ # the top-level directory. # Usage: -# clean-includes [--git subjectprefix] file ... +# clean-includes [--git subjectprefix] [--check-dup-head] file ... # or -# clean-includes [--git subjectprefix] --all +# clean-includes [--git subjectprefix] [--check-dup-head] --all # # If the --git subjectprefix option is given, then after making # the changes to the files this script will create a git commit # with the subject line "subjectprefix: Clean up includes" # and a boilerplate commit message. # +# If --check-dup-head is specified, additionally check for duplicate +# header includes. +# # Using --all will cause clean-includes to run on the whole source # tree (excluding certain directories which are known not to need # handling). @@ -45,23 +48,40 @@ GIT=no +DUPHEAD=no # Extended regular expression defining files to ignore when using --all XDIRREGEX='^(tests/tcg|tests/multiboot|pc-bios|disas/libvixl)' -if [ $# -ne 0 ] && [ "$1" = "--git" ]; then - if [ $# -eq 1 ]; then - echo "--git option requires an argument" - exit 1 - fi - GITSUBJ="$2" - GIT=yes - shift - shift -fi +while true +do + case $1 in + "--git") + if [ $# -eq 1 ]; then + echo "--git option requires an argument" + exit 1 + fi + GITSUBJ="$2" + GIT=yes + shift + shift + ;; + "--check-dup-head") + DUPHEAD=yes + shift + ;; + "--") + shift + break + ;; + *) + break + ;; + esac +done if [ $# -eq 0 ]; then - echo "Usage: clean-includes [--git subjectprefix] [--all | foo.c ...]" + echo "Usage: clean-includes [--git subjectprefix] [--check-dup-head] [--all | foo.c ...]" echo "(modifies the files in place)" exit 1 fi @@ -91,7 +111,6 @@ cat >"$COCCIFILE" <<EOT ) EOT - for f in "$@"; do case "$f" in *.inc.c) @@ -154,6 +173,15 @@ for f in "$@"; do done +if [ "$DUPHEAD" = "yes" ]; then + egrep "^[[:space:]]*#[[:space:]]*include" "$@" | tr -d '[:blank:]' \ + | sort | uniq -c | awk '{if ($1 > 1) print $0}' + if [ $? -eq 0 ]; then + echo "Found duplicate header file includes. Please check the above files manually." + exit 1 + fi +fi + if [ "$GIT" = "yes" ]; then git add -- "$@" git commit --signoff -F - <<EOF diff --git a/scripts/hxtool b/scripts/hxtool index 995bb7f08c..04f7d7b0ed 100644 --- a/scripts/hxtool +++ b/scripts/hxtool @@ -26,32 +26,32 @@ hxtotexi() ;; STEXI*) if test $flag -eq 1 ; then - echo "line $line: syntax error: expected ETEXI, found $str" >&2 + printf "line %d: syntax error: expected ETEXI, found '%s'\n" "$line" "$str" >&2 exit 1 fi flag=1 ;; ETEXI*) if test $flag -ne 1 ; then - echo "line $line: syntax error: expected STEXI, found $str" >&2 + printf "line %d: syntax error: expected STEXI, found '%s'\n" "$line" "$str" >&2 exit 1 fi flag=0 ;; SQMP*|EQMP*) if test $flag -eq 1 ; then - echo "line $line: syntax error: expected ETEXI, found $str" >&2 + printf "line %d: syntax error: expected ETEXI, found '%s'\n" "$line" "$str" >&2 exit 1 fi ;; DEFHEADING*) - echo "$(expr "$str" : "DEFHEADING(\(.*\))")" + printf '%s\n' "$(expr "$str" : "DEFHEADING(\(.*\))")" ;; ARCHHEADING*) - echo "$(expr "$str" : "ARCHHEADING(\(.*\),.*)")" + printf '%s\n' "$(expr "$str" : "ARCHHEADING(\(.*\),.*)")" ;; *) - test $flag -eq 1 && echo "$str" + test $flag -eq 1 && printf '%s\n' "$str" ;; esac line=$((line+1)) @@ -69,26 +69,26 @@ hxtoqmp() ;; SQMP*) if test $flag -eq 1 ; then - echo "line $line: syntax error: expected EQMP, found $str" >&2 + printf "line %d: syntax error: expected EQMP, found '%s'\n" "$line" "$str" >&2 exit 1 fi flag=1 ;; EQMP*) if test $flag -ne 1 ; then - echo "line $line: syntax error: expected SQMP, found $str" >&2 + printf "line %d: syntax error: expected SQMP, found '%s'\n" "$line" "$str" >&2 exit 1 fi flag=0 ;; STEXI*|ETEXI*) if test $flag -eq 1 ; then - echo "line $line: syntax error: expected EQMP, found $str" >&2 + printf "line %d: syntax error: expected EQMP, found '%s'\n" "$line" "$str" >&2 exit 1 fi ;; *) - test $flag -eq 1 && echo "$str" + test $flag -eq 1 && printf '%s\n' "$str" ;; esac line=$((line+1)) diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 629b2593c8..fe9c9e904b 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -70,7 +70,7 @@ def make_group_name(filename): if dirname == "": return "common" - return re.sub(r"/|-", "_", dirname) + return re.sub(r"[^A-Za-z0-9]", "_", dirname) def main(args): global _SCRIPT diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 9885e83cd4..85f61028e2 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -21,7 +21,8 @@ PUBLIC = True def is_string(arg): strtype = ('const char*', 'char*', 'const char *', 'char *') - if arg.lstrip().startswith(strtype): + arg_strip = arg.lstrip() + if arg_strip.startswith(strtype) and arg_strip.count('*') == 1: return True else: return False diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index c5850e858e..7f236a7c1f 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -17,6 +17,7 @@ stub-obj-y += gdbstub.o stub-obj-y += get-fd.o stub-obj-y += get-next-serial.o stub-obj-y += get-vm-name.o +stub-obj-y += iothread.o stub-obj-y += iothread-lock.o stub-obj-y += is-daemonized.o stub-obj-y += machine-init-done.o @@ -48,3 +49,4 @@ stub-obj-y += iohandler.o stub-obj-y += smbios_type_38.o stub-obj-y += ipmi.o stub-obj-y += pc_madt_cpu_entry.o +stub-obj-y += migration-colo.o diff --git a/stubs/iothread.c b/stubs/iothread.c new file mode 100644 index 0000000000..8cc9e28c55 --- /dev/null +++ b/stubs/iothread.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "block/aio.h" +#include "qemu/main-loop.h" + +AioContext *qemu_get_current_aio_context(void) +{ + return qemu_get_aio_context(); +} diff --git a/stubs/migration-colo.c b/stubs/migration-colo.c new file mode 100644 index 0000000000..7811764c4b --- /dev/null +++ b/stubs/migration-colo.c @@ -0,0 +1,46 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "migration/colo.h" +#include "qmp-commands.h" + +bool colo_supported(void) +{ + return false; +} + +bool migration_in_colo_state(void) +{ + return false; +} + +bool migration_incoming_in_colo_state(void) +{ + return false; +} + +void migrate_start_colo_process(MigrationState *s) +{ +} + +void *colo_process_incoming_thread(void *opaque) +{ + return NULL; +} + +void qmp_x_colo_lost_heartbeat(Error **errp) +{ + error_setg(errp, "COLO is not supported, please rerun configure" + " with --enable-colo option in order to support" + " COLO feature"); +} diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 2439ca57d0..99f0dbebb9 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "cpu.h" #include "internals.h" @@ -496,6 +497,10 @@ static Property arm_cpu_rvbar_property = static Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); +/* use property name "pmu" to match other archs and virt tools */ +static Property arm_cpu_has_pmu_property = + DEFINE_PROP_BOOL("pmu", ARMCPU, has_pmu, true); + static Property arm_cpu_has_mpu_property = DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); @@ -539,6 +544,11 @@ static void arm_cpu_post_init(Object *obj) #endif } + if (arm_feature(&cpu->env, ARM_FEATURE_PMU)) { + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_pmu_property, + &error_abort); + } + if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property, &error_abort); @@ -677,6 +687,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->id_aa64pfr0 &= ~0xf000; } + if (!cpu->has_pmu || !kvm_enabled()) { + cpu->has_pmu = false; + unset_feature(env, ARM_FEATURE_PMU); + } + if (!arm_feature(env, ARM_FEATURE_EL2)) { /* Disable the hypervisor feature bits in the processor feature * registers if we don't have EL2. These are id_pfr1[15:12] and diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 19d967b69e..ca5c849ed6 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1124,6 +1124,7 @@ enum arm_features { ARM_FEATURE_V8_SHA256, /* implements SHA256 part of v8 Crypto Extensions */ ARM_FEATURE_V8_PMULL, /* implements PMULL part of v8 Crypto Extensions */ ARM_FEATURE_THUMB_DSP, /* DSP insns supported in the Thumb encodings */ + ARM_FEATURE_PMU, /* has PMU support */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 1635debc1a..549cb1ee93 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -111,6 +111,7 @@ static void aarch64_a57_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; cpu->midr = 0x411fd070; cpu->revidr = 0x00000000; @@ -166,6 +167,7 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; cpu->midr = 0x410fd034; cpu->revidr = 0x00000000; diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index 5faa76c57e..61111091ad 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -428,6 +428,11 @@ static inline void set_feature(uint64_t *features, int feature) *features |= 1ULL << feature; } +static inline void unset_feature(uint64_t *features, int feature) +{ + *features &= ~(1ULL << feature); +} + bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) { /* Identify the feature bits corresponding to the host CPU, and @@ -469,6 +474,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) set_feature(&features, ARM_FEATURE_VFP4); set_feature(&features, ARM_FEATURE_NEON); set_feature(&features, ARM_FEATURE_AARCH64); + set_feature(&features, ARM_FEATURE_PMU); ahcc->features = features; @@ -482,6 +488,7 @@ int kvm_arch_init_vcpu(CPUState *cs) int ret; uint64_t mpidr; ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { @@ -501,10 +508,14 @@ int kvm_arch_init_vcpu(CPUState *cs) if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT; } - if (kvm_irqchip_in_kernel() && - kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) { - cpu->has_pmu = true; + if (!kvm_irqchip_in_kernel() || + !kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) { + cpu->has_pmu = false; + } + if (cpu->has_pmu) { cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; + } else { + unset_feature(&env->features, ARM_FEATURE_PMU); } /* Do KVM_ARM_VCPU_INIT ioctl */ diff --git a/target-i386/machine.c b/target-i386/machine.c index 71c0e4dc47..48037f1575 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -7,10 +7,7 @@ #include "hw/i386/pc.h" #include "hw/isa/isa.h" #include "migration/cpu.h" -#include "exec/exec-all.h" -#include "cpu.h" -#include "exec/exec-all.h" #include "sysemu/kvm.h" #include "qemu/error-report.h" diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 2d8caebbfc..842af63a98 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -33,12 +33,14 @@ #include "exec/log.h" -#define DISAS_LM32 1 -#if DISAS_LM32 -# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) -#else -# define LOG_DIS(...) do { } while (0) -#endif +#define DISAS_LM32 0 + +#define LOG_DIS(...) \ + do { \ + if (DISAS_LM32) { \ + qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__); \ + } \ + } while (0) #define EXTRACT_FIELD(src, start, end) \ (((src) >> start) & ((1 << (end - start + 1)) - 1)) @@ -211,7 +213,7 @@ static void dec_and(DisasContext *dc) static void dec_andhi(DisasContext *dc) { - LOG_DIS("andhi r%d, r%d, %d\n", dc->r2, dc->r0, dc->imm16); + LOG_DIS("andhi r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm16); tcg_gen_andi_tl(cpu_R[dc->r1], cpu_R[dc->r0], (dc->imm16 << 16)); } @@ -274,7 +276,7 @@ static inline void gen_cond_branch(DisasContext *dc, int cond) static void dec_be(DisasContext *dc) { - LOG_DIS("be r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("be r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16) * 4); gen_cond_branch(dc, TCG_COND_EQ); @@ -282,7 +284,7 @@ static void dec_be(DisasContext *dc) static void dec_bg(DisasContext *dc) { - LOG_DIS("bg r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("bg r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16 * 4)); gen_cond_branch(dc, TCG_COND_GT); @@ -290,7 +292,7 @@ static void dec_bg(DisasContext *dc) static void dec_bge(DisasContext *dc) { - LOG_DIS("bge r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("bge r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16) * 4); gen_cond_branch(dc, TCG_COND_GE); @@ -298,7 +300,7 @@ static void dec_bge(DisasContext *dc) static void dec_bgeu(DisasContext *dc) { - LOG_DIS("bgeu r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("bgeu r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16) * 4); gen_cond_branch(dc, TCG_COND_GEU); @@ -306,7 +308,7 @@ static void dec_bgeu(DisasContext *dc) static void dec_bgu(DisasContext *dc) { - LOG_DIS("bgu r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("bgu r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16) * 4); gen_cond_branch(dc, TCG_COND_GTU); @@ -314,7 +316,7 @@ static void dec_bgu(DisasContext *dc) static void dec_bne(DisasContext *dc) { - LOG_DIS("bne r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("bne r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16) * 4); gen_cond_branch(dc, TCG_COND_NE); @@ -342,9 +344,6 @@ static void dec_calli(DisasContext *dc) static inline void gen_compare(DisasContext *dc, int cond) { - int rX = (dc->format == OP_FMT_RR) ? dc->r2 : dc->r1; - int rY = (dc->format == OP_FMT_RR) ? dc->r0 : dc->r0; - int rZ = (dc->format == OP_FMT_RR) ? dc->r1 : -1; int i; if (dc->format == OP_FMT_RI) { @@ -358,16 +357,16 @@ static inline void gen_compare(DisasContext *dc, int cond) break; } - tcg_gen_setcondi_tl(cond, cpu_R[rX], cpu_R[rY], i); + tcg_gen_setcondi_tl(cond, cpu_R[dc->r1], cpu_R[dc->r0], i); } else { - tcg_gen_setcond_tl(cond, cpu_R[rX], cpu_R[rY], cpu_R[rZ]); + tcg_gen_setcond_tl(cond, cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); } } static void dec_cmpe(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("cmpei r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("cmpei r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); } else { LOG_DIS("cmpe r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -379,7 +378,7 @@ static void dec_cmpe(DisasContext *dc) static void dec_cmpg(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("cmpgi r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("cmpgi r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); } else { LOG_DIS("cmpg r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -391,7 +390,7 @@ static void dec_cmpg(DisasContext *dc) static void dec_cmpge(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("cmpgei r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("cmpgei r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); } else { LOG_DIS("cmpge r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -403,7 +402,7 @@ static void dec_cmpge(DisasContext *dc) static void dec_cmpgeu(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("cmpgeui r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("cmpgeui r%d, r%d, %d\n", dc->r1, dc->r0, zero_extend(dc->imm16, 16)); } else { LOG_DIS("cmpgeu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -415,7 +414,7 @@ static void dec_cmpgeu(DisasContext *dc) static void dec_cmpgu(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("cmpgui r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("cmpgui r%d, r%d, %d\n", dc->r1, dc->r0, zero_extend(dc->imm16, 16)); } else { LOG_DIS("cmpgu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -427,7 +426,7 @@ static void dec_cmpgu(DisasContext *dc) static void dec_cmpne(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("cmpnei r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("cmpnei r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); } else { LOG_DIS("cmpne r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -539,7 +538,7 @@ static void dec_modu(DisasContext *dc) static void dec_mul(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("muli r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("muli r%d, r%d, %d\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); } else { LOG_DIS("mul r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -563,7 +562,7 @@ static void dec_mul(DisasContext *dc) static void dec_nor(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("nori r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("nori r%d, r%d, %d\n", dc->r1, dc->r0, zero_extend(dc->imm16, 16)); } else { LOG_DIS("nor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -865,7 +864,7 @@ static void dec_wcsr(DisasContext *dc) { int no; - LOG_DIS("wcsr r%d, %d\n", dc->r1, dc->csr); + LOG_DIS("wcsr %d, r%d\n", dc->csr, dc->r1); switch (dc->csr) { case CSR_IE: @@ -959,7 +958,7 @@ static void dec_wcsr(DisasContext *dc) static void dec_xnor(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("xnori r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("xnori r%d, r%d, %d\n", dc->r1, dc->r0, zero_extend(dc->imm16, 16)); } else { if (dc->r1 == R_R0) { @@ -981,7 +980,7 @@ static void dec_xnor(DisasContext *dc) static void dec_xor(DisasContext *dc) { if (dc->format == OP_FMT_RI) { - LOG_DIS("xori r%d, r%d, %d\n", dc->r0, dc->r1, + LOG_DIS("xori r%d, r%d, %d\n", dc->r1, dc->r0, zero_extend(dc->imm16, 16)); } else { LOG_DIS("xor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index 17e4be2934..ba17480098 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -58,15 +58,20 @@ static void m68k_cpu_reset(CPUState *s) #endif m68k_switch_sp(env); /* ??? FP regs should be initialized to NaN. */ - env->cc_op = CC_OP_FLAGS; + cpu_m68k_set_ccr(env, 0); /* TODO: We should set PC from the interrupt vector. */ env->pc = 0; tlb_flush(s, 1); } -static void m68k_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +static void m68k_cpu_disas_set_info(CPUState *s, disassemble_info *info) { + M68kCPU *cpu = M68K_CPU(s); + CPUM68KState *env = &cpu->env; info->print_insn = print_insn_m68k; + if (m68k_feature(env, M68K_FEATURE_M68000)) { + info->mach = bfd_mach_m68040; + } } /* CPU models */ @@ -98,6 +103,57 @@ static void m5206_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); } +static void m68000_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); +} + +static void m68020_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); + m68k_set_feature(env, M68K_FEATURE_QUAD_MULDIV); + m68k_set_feature(env, M68K_FEATURE_BRAL); + m68k_set_feature(env, M68K_FEATURE_BCCL); + m68k_set_feature(env, M68K_FEATURE_BITFIELD); + m68k_set_feature(env, M68K_FEATURE_EXT_FULL); + m68k_set_feature(env, M68K_FEATURE_SCALED_INDEX); + m68k_set_feature(env, M68K_FEATURE_LONG_MULDIV); + m68k_set_feature(env, M68K_FEATURE_FPU); + m68k_set_feature(env, M68K_FEATURE_CAS); + m68k_set_feature(env, M68K_FEATURE_BKPT); +} +#define m68030_cpu_initfn m68020_cpu_initfn +#define m68040_cpu_initfn m68020_cpu_initfn + +static void m68060_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); + m68k_set_feature(env, M68K_FEATURE_BRAL); + m68k_set_feature(env, M68K_FEATURE_BCCL); + m68k_set_feature(env, M68K_FEATURE_BITFIELD); + m68k_set_feature(env, M68K_FEATURE_EXT_FULL); + m68k_set_feature(env, M68K_FEATURE_SCALED_INDEX); + m68k_set_feature(env, M68K_FEATURE_LONG_MULDIV); + m68k_set_feature(env, M68K_FEATURE_FPU); + m68k_set_feature(env, M68K_FEATURE_CAS); + m68k_set_feature(env, M68K_FEATURE_BKPT); +} + static void m5208_cpu_initfn(Object *obj) { M68kCPU *cpu = M68K_CPU(obj); @@ -148,6 +204,11 @@ typedef struct M68kCPUInfo { } M68kCPUInfo; static const M68kCPUInfo m68k_cpus[] = { + { .name = "m68000", .instance_init = m68000_cpu_initfn }, + { .name = "m68020", .instance_init = m68020_cpu_initfn }, + { .name = "m68030", .instance_init = m68030_cpu_initfn }, + { .name = "m68040", .instance_init = m68040_cpu_initfn }, + { .name = "m68060", .instance_init = m68060_cpu_initfn }, { .name = "m5206", .instance_init = m5206_cpu_initfn }, { .name = "m5208", .instance_init = m5208_cpu_initfn }, { .name = "cfv4e", .instance_init = cfv4e_cpu_initfn }, @@ -220,8 +281,6 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) #else cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug; #endif - cc->cpu_exec_enter = m68k_cpu_exec_enter; - cc->cpu_exec_exit = m68k_cpu_exec_exit; cc->disas_set_info = m68k_cpu_disas_set_info; cc->gdb_num_core_regs = 18; diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index 471f490dc1..6dfb54eb70 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -30,6 +30,14 @@ #include "cpu-qom.h" #include "fpu/softfloat.h" +#define OS_BYTE 0 +#define OS_WORD 1 +#define OS_LONG 2 +#define OS_SINGLE 3 +#define OS_DOUBLE 4 +#define OS_EXTENDED 5 +#define OS_PACKED 6 + #define MAX_QREGS 32 #define EXCP_ACCESS 2 /* Access (MMU) error. */ @@ -53,6 +61,7 @@ #define EXCP_HALT_INSN 0x101 #define NB_MMU_MODES 2 +#define TARGET_INSN_START_EXTRA_WORDS 1 typedef struct CPUM68KState { uint32_t dregs[8]; @@ -66,9 +75,11 @@ typedef struct CPUM68KState { /* Condition flags. */ uint32_t cc_op; - uint32_t cc_dest; - uint32_t cc_src; - uint32_t cc_x; + uint32_t cc_x; /* always 0/1 */ + uint32_t cc_n; /* in bit 31 (i.e. negative) */ + uint32_t cc_v; /* in bit 31, unused, or computed from cc_n and cc_v */ + uint32_t cc_c; /* either 0/1, unused, or computed from cc_n and cc_v */ + uint32_t cc_z; /* == 0 or unused */ float64 fregs[8]; float64 fp_result; @@ -141,9 +152,6 @@ hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -void m68k_cpu_exec_enter(CPUState *cs); -void m68k_cpu_exec_exit(CPUState *cs); - void m68k_tcg_init(void); void m68k_cpu_init_gdb(M68kCPU *cpu); M68kCPU *cpu_m68k_init(const char *cpu_model); @@ -152,7 +160,8 @@ M68kCPU *cpu_m68k_init(const char *cpu_model); is returned if the signal was handled by the virtual CPU. */ int cpu_m68k_signal_handler(int host_signum, void *pinfo, void *puc); -void cpu_m68k_flush_flags(CPUM68KState *, int); +uint32_t cpu_m68k_get_ccr(CPUM68KState *env); +void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); /* Instead of computing the condition codes after each m68k instruction, @@ -162,18 +171,25 @@ void cpu_m68k_flush_flags(CPUM68KState *, int); * using this information. Condition codes are not generated if they * are only needed for conditional branches. */ -enum { - CC_OP_DYNAMIC, /* Use env->cc_op */ - CC_OP_FLAGS, /* CC_DEST = CVZN, CC_SRC = unused */ - CC_OP_LOGIC, /* CC_DEST = result, CC_SRC = unused */ - CC_OP_ADD, /* CC_DEST = result, CC_SRC = source */ - CC_OP_SUB, /* CC_DEST = result, CC_SRC = source */ - CC_OP_CMPB, /* CC_DEST = result, CC_SRC = source */ - CC_OP_CMPW, /* CC_DEST = result, CC_SRC = source */ - CC_OP_ADDX, /* CC_DEST = result, CC_SRC = source */ - CC_OP_SUBX, /* CC_DEST = result, CC_SRC = source */ - CC_OP_SHIFT, /* CC_DEST = result, CC_SRC = carry */ -}; +typedef enum { + /* Translator only -- use env->cc_op. */ + CC_OP_DYNAMIC = -1, + + /* Each flag bit computed into cc_[xcnvz]. */ + CC_OP_FLAGS, + + /* X in cc_x, C = X, N in cc_n, Z in cc_n, V via cc_n/cc_v. */ + CC_OP_ADDB, CC_OP_ADDW, CC_OP_ADDL, + CC_OP_SUBB, CC_OP_SUBW, CC_OP_SUBL, + + /* X in cc_x, {N,Z,C,V} via cc_n/cc_v. */ + CC_OP_CMPB, CC_OP_CMPW, CC_OP_CMPL, + + /* X in cc_x, C = 0, V = 0, N in cc_n, Z in cc_n. */ + CC_OP_LOGIC, + + CC_OP_NB +} CCOp; #define CCF_C 0x01 #define CCF_V 0x02 @@ -215,6 +231,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr); ISA revisions mentioned. */ enum m68k_features { + M68K_FEATURE_M68000, M68K_FEATURE_CF_ISA_A, M68K_FEATURE_CF_ISA_B, /* (ISA B or C). */ M68K_FEATURE_CF_ISA_APLUSC, /* BIT/BITREV, FF1, STRLDSR (ISA A+ or C). */ @@ -225,7 +242,15 @@ enum m68k_features { M68K_FEATURE_CF_EMAC_B, /* Revision B EMAC (dual accumulate). */ M68K_FEATURE_USP, /* User Stack Pointer. (ISA A+, B or C). */ M68K_FEATURE_EXT_FULL, /* 68020+ full extension word. */ - M68K_FEATURE_WORD_INDEX /* word sized address index registers. */ + M68K_FEATURE_WORD_INDEX, /* word sized address index registers. */ + M68K_FEATURE_SCALED_INDEX, /* scaled address index registers. */ + M68K_FEATURE_LONG_MULDIV, /* 32 bit multiply/divide. */ + M68K_FEATURE_QUAD_MULDIV, /* 64 bit multiply/divide. */ + M68K_FEATURE_BCCL, /* Long conditional branches. */ + M68K_FEATURE_BITFIELD, /* Bit field insns. */ + M68K_FEATURE_FPU, + M68K_FEATURE_CAS, + M68K_FEATURE_BKPT, }; static inline int m68k_feature(CPUM68KState *env, int feature) @@ -238,8 +263,11 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf); void register_m68k_insns (CPUM68KState *env); #ifdef CONFIG_USER_ONLY -/* Linux uses 8k pages. */ -#define TARGET_PAGE_BITS 13 +/* Coldfire Linux uses 8k pages + * and m68k linux uses 4k pages + * use the smaller one + */ +#define TARGET_PAGE_BITS 12 #else /* Smallest TLB entry size is 1k. */ #define TARGET_PAGE_BITS 10 diff --git a/target-m68k/helper.c b/target-m68k/helper.c index 89bbe6dfa6..7aed9ffd2f 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -132,87 +132,6 @@ void m68k_cpu_init_gdb(M68kCPU *cpu) /* TODO: Add [E]MAC registers. */ } -void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op) -{ - M68kCPU *cpu = m68k_env_get_cpu(env); - int flags; - uint32_t src; - uint32_t dest; - uint32_t tmp; - -#define HIGHBIT 0x80000000u - -#define SET_NZ(x) do { \ - if ((x) == 0) \ - flags |= CCF_Z; \ - else if ((int32_t)(x) < 0) \ - flags |= CCF_N; \ - } while (0) - -#define SET_FLAGS_SUB(type, utype) do { \ - SET_NZ((type)dest); \ - tmp = dest + src; \ - if ((utype) tmp < (utype) src) \ - flags |= CCF_C; \ - if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \ - flags |= CCF_V; \ - } while (0) - - flags = 0; - src = env->cc_src; - dest = env->cc_dest; - switch (cc_op) { - case CC_OP_FLAGS: - flags = dest; - break; - case CC_OP_LOGIC: - SET_NZ(dest); - break; - case CC_OP_ADD: - SET_NZ(dest); - if (dest < src) - flags |= CCF_C; - tmp = dest - src; - if (HIGHBIT & (src ^ dest) & ~(tmp ^ src)) - flags |= CCF_V; - break; - case CC_OP_SUB: - SET_FLAGS_SUB(int32_t, uint32_t); - break; - case CC_OP_CMPB: - SET_FLAGS_SUB(int8_t, uint8_t); - break; - case CC_OP_CMPW: - SET_FLAGS_SUB(int16_t, uint16_t); - break; - case CC_OP_ADDX: - SET_NZ(dest); - if (dest <= src) - flags |= CCF_C; - tmp = dest - src - 1; - if (HIGHBIT & (src ^ dest) & ~(tmp ^ src)) - flags |= CCF_V; - break; - case CC_OP_SUBX: - SET_NZ(dest); - tmp = dest + src + 1; - if (tmp <= src) - flags |= CCF_C; - if (HIGHBIT & (tmp ^ dest) & (tmp ^ src)) - flags |= CCF_V; - break; - case CC_OP_SHIFT: - SET_NZ(dest); - if (src) - flags |= CCF_C; - break; - default: - cpu_abort(CPU(cpu), "Bad CC_OP %d", cc_op); - } - env->cc_op = CC_OP_FLAGS; - env->cc_dest = flags; -} - void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val) { M68kCPU *cpu = m68k_env_get_cpu(env); @@ -349,140 +268,71 @@ uint32_t HELPER(ff1)(uint32_t x) return n; } -uint32_t HELPER(sats)(uint32_t val, uint32_t ccr) +uint32_t HELPER(sats)(uint32_t val, uint32_t v) { /* The result has the opposite sign to the original value. */ - if (ccr & CCF_V) + if ((int32_t)v < 0) { val = (((int32_t)val) >> 31) ^ SIGNBIT; - return val; -} - -uint32_t HELPER(subx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2) -{ - uint32_t res; - uint32_t old_flags; - - old_flags = env->cc_dest; - if (env->cc_x) { - env->cc_x = (op1 <= op2); - env->cc_op = CC_OP_SUBX; - res = op1 - (op2 + 1); - } else { - env->cc_x = (op1 < op2); - env->cc_op = CC_OP_SUB; - res = op1 - op2; - } - env->cc_dest = res; - env->cc_src = op2; - cpu_m68k_flush_flags(env, env->cc_op); - /* !Z is sticky. */ - env->cc_dest &= (old_flags | ~CCF_Z); - return res; -} - -uint32_t HELPER(addx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2) -{ - uint32_t res; - uint32_t old_flags; - - old_flags = env->cc_dest; - if (env->cc_x) { - res = op1 + op2 + 1; - env->cc_x = (res <= op2); - env->cc_op = CC_OP_ADDX; - } else { - res = op1 + op2; - env->cc_x = (res < op2); - env->cc_op = CC_OP_ADD; } - env->cc_dest = res; - env->cc_src = op2; - cpu_m68k_flush_flags(env, env->cc_op); - /* !Z is sticky. */ - env->cc_dest &= (old_flags | ~CCF_Z); - return res; -} - -uint32_t HELPER(xflag_lt)(uint32_t a, uint32_t b) -{ - return a < b; + return val; } void HELPER(set_sr)(CPUM68KState *env, uint32_t val) { - env->sr = val & 0xffff; + env->sr = val & 0xffe0; + cpu_m68k_set_ccr(env, val); m68k_switch_sp(env); } uint32_t HELPER(shl_cc)(CPUM68KState *env, uint32_t val, uint32_t shift) { - uint32_t result; - uint32_t cf; + uint64_t result; shift &= 63; - if (shift == 0) { - result = val; - cf = env->cc_src & CCF_C; - } else if (shift < 32) { - result = val << shift; - cf = (val >> (32 - shift)) & 1; - } else if (shift == 32) { - result = 0; - cf = val & 1; - } else /* shift > 32 */ { - result = 0; - cf = 0; - } - env->cc_src = cf; - env->cc_x = (cf != 0); - env->cc_dest = result; + result = (uint64_t)val << shift; + + env->cc_c = (result >> 32) & 1; + env->cc_n = result; + env->cc_z = result; + env->cc_v = 0; + env->cc_x = shift ? env->cc_c : env->cc_x; + return result; } uint32_t HELPER(shr_cc)(CPUM68KState *env, uint32_t val, uint32_t shift) { + uint64_t temp; uint32_t result; - uint32_t cf; shift &= 63; - if (shift == 0) { - result = val; - cf = env->cc_src & CCF_C; - } else if (shift < 32) { - result = val >> shift; - cf = (val >> (shift - 1)) & 1; - } else if (shift == 32) { - result = 0; - cf = val >> 31; - } else /* shift > 32 */ { - result = 0; - cf = 0; - } - env->cc_src = cf; - env->cc_x = (cf != 0); - env->cc_dest = result; + temp = (uint64_t)val << 32 >> shift; + result = temp >> 32; + + env->cc_c = (temp >> 31) & 1; + env->cc_n = result; + env->cc_z = result; + env->cc_v = 0; + env->cc_x = shift ? env->cc_c : env->cc_x; + return result; } uint32_t HELPER(sar_cc)(CPUM68KState *env, uint32_t val, uint32_t shift) { + uint64_t temp; uint32_t result; - uint32_t cf; shift &= 63; - if (shift == 0) { - result = val; - cf = (env->cc_src & CCF_C) != 0; - } else if (shift < 32) { - result = (int32_t)val >> shift; - cf = (val >> (shift - 1)) & 1; - } else /* shift >= 32 */ { - result = (int32_t)val >> 31; - cf = val >> 31; - } - env->cc_src = cf; - env->cc_x = cf; - env->cc_dest = result; + temp = (int64_t)val << 32 >> shift; + result = temp >> 32; + + env->cc_c = (temp >> 31) & 1; + env->cc_n = result; + env->cc_z = result; + env->cc_v = result ^ val; + env->cc_x = shift ? env->cc_c : env->cc_x; + return result; } @@ -734,9 +584,101 @@ void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc) } } +#define EXTSIGN(val, index) ( \ + (index == 0) ? (int8_t)(val) : ((index == 1) ? (int16_t)(val) : (val)) \ +) + +#define COMPUTE_CCR(op, x, n, z, v, c) { \ + switch (op) { \ + case CC_OP_FLAGS: \ + /* Everything in place. */ \ + break; \ + case CC_OP_ADDB: \ + case CC_OP_ADDW: \ + case CC_OP_ADDL: \ + res = n; \ + src2 = v; \ + src1 = EXTSIGN(res - src2, op - CC_OP_ADDB); \ + c = x; \ + z = n; \ + v = (res ^ src1) & ~(src1 ^ src2); \ + break; \ + case CC_OP_SUBB: \ + case CC_OP_SUBW: \ + case CC_OP_SUBL: \ + res = n; \ + src2 = v; \ + src1 = EXTSIGN(res + src2, op - CC_OP_SUBB); \ + c = x; \ + z = n; \ + v = (res ^ src1) & (src1 ^ src2); \ + break; \ + case CC_OP_CMPB: \ + case CC_OP_CMPW: \ + case CC_OP_CMPL: \ + src1 = n; \ + src2 = v; \ + res = EXTSIGN(src1 - src2, op - CC_OP_CMPB); \ + n = res; \ + z = res; \ + c = src1 < src2; \ + v = (res ^ src1) & (src1 ^ src2); \ + break; \ + case CC_OP_LOGIC: \ + c = v = 0; \ + z = n; \ + break; \ + default: \ + cpu_abort(CPU(m68k_env_get_cpu(env)), "Bad CC_OP %d", op); \ + } \ +} while (0) + +uint32_t cpu_m68k_get_ccr(CPUM68KState *env) +{ + uint32_t x, c, n, z, v; + uint32_t res, src1, src2; + + x = env->cc_x; + n = env->cc_n; + z = env->cc_z; + v = env->cc_v; + c = env->cc_c; + + COMPUTE_CCR(env->cc_op, x, n, z, v, c); + + n = n >> 31; + z = (z == 0); + v = v >> 31; + + return x * CCF_X + n * CCF_N + z * CCF_Z + v * CCF_V + c * CCF_C; +} + +uint32_t HELPER(get_ccr)(CPUM68KState *env) +{ + return cpu_m68k_get_ccr(env); +} + +void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t ccr) +{ + env->cc_x = (ccr & CCF_X ? 1 : 0); + env->cc_n = (ccr & CCF_N ? -1 : 0); + env->cc_z = (ccr & CCF_Z ? 0 : 1); + env->cc_v = (ccr & CCF_V ? -1 : 0); + env->cc_c = (ccr & CCF_C ? 1 : 0); + env->cc_op = CC_OP_FLAGS; +} + +void HELPER(set_ccr)(CPUM68KState *env, uint32_t ccr) +{ + cpu_m68k_set_ccr(env, ccr); +} + void HELPER(flush_flags)(CPUM68KState *env, uint32_t cc_op) { - cpu_m68k_flush_flags(env, cc_op); + uint32_t res, src1, src2; + + COMPUTE_CCR(cc_op, env->cc_x, env->cc_n, env->cc_z, env->cc_v, env->cc_c); + env->cc_op = CC_OP_FLAGS; } uint32_t HELPER(get_macf)(CPUM68KState *env, uint64_t val) @@ -866,23 +808,3 @@ void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc) res |= (uint64_t)(val & 0xffff0000) << 16; env->macc[acc + 1] = res; } - -void m68k_cpu_exec_enter(CPUState *cs) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - env->cc_op = CC_OP_FLAGS; - env->cc_dest = env->sr & 0xf; - env->cc_x = (env->sr >> 4) & 1; -} - -void m68k_cpu_exec_exit(CPUState *cs) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - cpu_m68k_flush_flags(env, env->cc_op); - env->cc_op = CC_OP_FLAGS; - env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); -} diff --git a/target-m68k/helper.h b/target-m68k/helper.h index f4e5fdf021..2697e32d0b 100644 --- a/target-m68k/helper.h +++ b/target-m68k/helper.h @@ -1,14 +1,11 @@ DEF_HELPER_1(bitrev, i32, i32) DEF_HELPER_1(ff1, i32, i32) -DEF_HELPER_2(sats, i32, i32, i32) +DEF_HELPER_FLAGS_2(sats, TCG_CALL_NO_RWG_SE, i32, i32, i32) DEF_HELPER_2(divu, void, env, i32) DEF_HELPER_2(divs, void, env, i32) -DEF_HELPER_3(addx_cc, i32, env, i32, i32) -DEF_HELPER_3(subx_cc, i32, env, i32, i32) DEF_HELPER_3(shl_cc, i32, env, i32, i32) DEF_HELPER_3(shr_cc, i32, env, i32, i32) DEF_HELPER_3(sar_cc, i32, env, i32, i32) -DEF_HELPER_2(xflag_lt, i32, i32, i32) DEF_HELPER_2(set_sr, void, env, i32) DEF_HELPER_3(movec, void, env, i32, i32) @@ -47,4 +44,6 @@ DEF_HELPER_3(set_mac_exts, void, env, i32, i32) DEF_HELPER_3(set_mac_extu, void, env, i32, i32) DEF_HELPER_2(flush_flags, void, env, i32) +DEF_HELPER_2(set_ccr, void, env, i32) +DEF_HELPER_FLAGS_1(get_ccr, TCG_CALL_NO_WG_SE, i32, env) DEF_HELPER_2(raise_exception, void, env, i32) diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c index e41ae46498..48e02e4062 100644 --- a/target-m68k/op_helper.c +++ b/target-m68k/op_helper.c @@ -63,9 +63,9 @@ static void do_rte(CPUM68KState *env) fmt = cpu_ldl_kernel(env, sp); env->pc = cpu_ldl_kernel(env, sp + 4); sp |= (fmt >> 28) & 3; - env->sr = fmt & 0xffff; env->aregs[7] = sp + 8; - m68k_switch_sp(env); + + helper_set_sr(env, fmt); } static void do_interrupt_all(CPUM68KState *env, int is_hw) @@ -112,6 +112,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) fmt |= 0x40000000; fmt |= vector << 16; fmt |= env->sr; + fmt |= cpu_m68k_get_ccr(env); env->sr |= SR_S; if (is_hw) { @@ -184,7 +185,6 @@ void HELPER(divu)(CPUM68KState *env, uint32_t word) uint32_t den; uint32_t quot; uint32_t rem; - uint32_t flags; num = env->div1; den = env->div2; @@ -194,16 +194,14 @@ void HELPER(divu)(CPUM68KState *env, uint32_t word) } quot = num / den; rem = num % den; - flags = 0; - if (word && quot > 0xffff) - flags |= CCF_V; - if (quot == 0) - flags |= CCF_Z; - else if ((int32_t)quot < 0) - flags |= CCF_N; + + env->cc_v = (word && quot > 0xffff ? -1 : 0); + env->cc_z = quot; + env->cc_n = quot; + env->cc_c = 0; + env->div1 = quot; env->div2 = rem; - env->cc_dest = flags; } void HELPER(divs)(CPUM68KState *env, uint32_t word) @@ -212,7 +210,6 @@ void HELPER(divs)(CPUM68KState *env, uint32_t word) int32_t den; int32_t quot; int32_t rem; - int32_t flags; num = env->div1; den = env->div2; @@ -221,14 +218,12 @@ void HELPER(divs)(CPUM68KState *env, uint32_t word) } quot = num / den; rem = num % den; - flags = 0; - if (word && quot != (int16_t)quot) - flags |= CCF_V; - if (quot == 0) - flags |= CCF_Z; - else if (quot < 0) - flags |= CCF_N; + + env->cc_v = (word && quot != (int16_t)quot ? -1 : 0); + env->cc_z = quot; + env->cc_n = quot; + env->cc_c = 0; + env->div1 = quot; env->div2 = rem; - env->cc_dest = flags; } diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def index 204663e1aa..156c0f558f 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -2,9 +2,11 @@ DEFF64(FP_RESULT, fp_result) DEFO32(PC, pc) DEFO32(SR, sr) DEFO32(CC_OP, cc_op) -DEFO32(CC_DEST, cc_dest) -DEFO32(CC_SRC, cc_src) DEFO32(CC_X, cc_x) +DEFO32(CC_C, cc_c) +DEFO32(CC_N, cc_n) +DEFO32(CC_V, cc_v) +DEFO32(CC_Z, cc_z) DEFO32(DIV1, div1) DEFO32(DIV2, div2) DEFO32(MACSR, macsr) diff --git a/target-m68k/translate.c b/target-m68k/translate.c index ecd5e5c8fd..ee0ffe3e07 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -59,9 +59,10 @@ static TCGv cpu_aregs[8]; static TCGv_i64 cpu_fregs[8]; static TCGv_i64 cpu_macc[4]; -#define DREG(insn, pos) cpu_dregs[((insn) >> (pos)) & 7] -#define AREG(insn, pos) cpu_aregs[((insn) >> (pos)) & 7] -#define FREG(insn, pos) cpu_fregs[((insn) >> (pos)) & 7] +#define REG(insn, pos) (((insn) >> (pos)) & 7) +#define DREG(insn, pos) cpu_dregs[REG(insn, pos)] +#define AREG(insn, pos) cpu_aregs[REG(insn, pos)] +#define FREG(insn, pos) cpu_fregs[REG(insn, pos)] #define MACREG(acc) cpu_macc[acc] #define QREG_SP cpu_aregs[7] @@ -132,7 +133,8 @@ typedef struct DisasContext { target_ulong insn_pc; /* Start of the current instruction. */ target_ulong pc; int is_jmp; - int cc_op; + CCOp cc_op; /* Current CC operation */ + int cc_op_synced; int user; uint32_t fpcr; struct TranslationBlock *tb; @@ -154,12 +156,6 @@ typedef struct DisasContext { static void *gen_throws_exception; #define gen_last_qop NULL -#define OS_BYTE 0 -#define OS_WORD 1 -#define OS_LONG 2 -#define OS_SINGLE 4 -#define OS_DOUBLE 5 - typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn); #ifdef DEBUG_DISPATCH @@ -170,7 +166,7 @@ typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn); uint16_t insn) \ { \ qemu_log("Dispatch " #name "\n"); \ - real_disas_##name(s, env, insn); \ + real_disas_##name(env, s, insn); \ } \ static void real_disas_##name(CPUM68KState *env, DisasContext *s, \ uint16_t insn) @@ -180,6 +176,48 @@ typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn); uint16_t insn) #endif +static const uint8_t cc_op_live[CC_OP_NB] = { + [CC_OP_FLAGS] = CCF_C | CCF_V | CCF_Z | CCF_N | CCF_X, + [CC_OP_ADDB ... CC_OP_ADDL] = CCF_X | CCF_N | CCF_V, + [CC_OP_SUBB ... CC_OP_SUBL] = CCF_X | CCF_N | CCF_V, + [CC_OP_CMPB ... CC_OP_CMPL] = CCF_X | CCF_N | CCF_V, + [CC_OP_LOGIC] = CCF_X | CCF_N +}; + +static void set_cc_op(DisasContext *s, CCOp op) +{ + CCOp old_op = s->cc_op; + int dead; + + if (old_op == op) { + return; + } + s->cc_op = op; + s->cc_op_synced = 0; + + /* Discard CC computation that will no longer be used. + Note that X and N are never dead. */ + dead = cc_op_live[old_op] & ~cc_op_live[op]; + if (dead & CCF_C) { + tcg_gen_discard_i32(QREG_CC_C); + } + if (dead & CCF_Z) { + tcg_gen_discard_i32(QREG_CC_Z); + } + if (dead & CCF_V) { + tcg_gen_discard_i32(QREG_CC_V); + } +} + +/* Update the CPU env CC_OP state. */ +static void update_cc_op(DisasContext *s) +{ + if (!s->cc_op_synced) { + s->cc_op_synced = 1; + tcg_gen_movi_i32(QREG_CC_OP, s->cc_op); + } +} + /* Generate a load from the specified address. Narrow values are sign extended to full register width. */ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign) @@ -268,14 +306,27 @@ static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val, } } +/* Read a 16-bit immediate constant */ +static inline uint16_t read_im16(CPUM68KState *env, DisasContext *s) +{ + uint16_t im; + im = cpu_lduw_code(env, s->pc); + s->pc += 2; + return im; +} + +/* Read an 8-bit immediate constant */ +static inline uint8_t read_im8(CPUM68KState *env, DisasContext *s) +{ + return read_im16(env, s); +} + /* Read a 32-bit immediate constant. */ static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s) { uint32_t im; - im = ((uint32_t)cpu_lduw_code(env, s->pc)) << 16; - s->pc += 2; - im |= cpu_lduw_code(env, s->pc); - s->pc += 2; + im = read_im16(env, s) << 16; + im |= 0xffff & read_im16(env, s); return im; } @@ -309,12 +360,16 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) uint32_t bd, od; offset = s->pc; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX)) return NULL_QREG; + if (m68k_feature(s->env, M68K_FEATURE_M68000) && + !m68k_feature(s->env, M68K_FEATURE_SCALED_INDEX)) { + ext &= ~(3 << 9); + } + if (ext & 0x100) { /* full extension word format */ if (!m68k_feature(s->env, M68K_FEATURE_EXT_FULL)) @@ -323,8 +378,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) if ((ext & 0x30) > 0x10) { /* base displacement */ if ((ext & 0x30) == 0x20) { - bd = (int16_t)cpu_lduw_code(env, s->pc); - s->pc += 2; + bd = (int16_t)read_im16(env, s); } else { bd = read_im32(env, s); } @@ -372,8 +426,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) if ((ext & 3) > 1) { /* outer displacement */ if ((ext & 3) == 2) { - od = (int16_t)cpu_lduw_code(env, s->pc); - s->pc += 2; + od = (int16_t)read_im16(env, s); } else { od = read_im32(env, s); } @@ -401,33 +454,145 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) return add; } -/* Update the CPU env CC_OP state. */ -static inline void gen_flush_cc_op(DisasContext *s) +/* Sign or zero extend a value. */ + +static inline void gen_ext(TCGv res, TCGv val, int opsize, int sign) { - if (s->cc_op != CC_OP_DYNAMIC) - tcg_gen_movi_i32(QREG_CC_OP, s->cc_op); + switch (opsize) { + case OS_BYTE: + if (sign) { + tcg_gen_ext8s_i32(res, val); + } else { + tcg_gen_ext8u_i32(res, val); + } + break; + case OS_WORD: + if (sign) { + tcg_gen_ext16s_i32(res, val); + } else { + tcg_gen_ext16u_i32(res, val); + } + break; + case OS_LONG: + tcg_gen_mov_i32(res, val); + break; + default: + g_assert_not_reached(); + } } /* Evaluate all the CC flags. */ -static inline void gen_flush_flags(DisasContext *s) + +static void gen_flush_flags(DisasContext *s) { - if (s->cc_op == CC_OP_FLAGS) + TCGv t0, t1; + + switch (s->cc_op) { + case CC_OP_FLAGS: return; - gen_flush_cc_op(s); - gen_helper_flush_flags(cpu_env, QREG_CC_OP); + + case CC_OP_ADDB: + case CC_OP_ADDW: + case CC_OP_ADDL: + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); + /* Compute signed overflow for addition. */ + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_sub_i32(t0, QREG_CC_N, QREG_CC_V); + gen_ext(t0, t0, s->cc_op - CC_OP_ADDB, 1); + tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V); + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0); + tcg_temp_free(t0); + tcg_gen_andc_i32(QREG_CC_V, t1, QREG_CC_V); + tcg_temp_free(t1); + break; + + case CC_OP_SUBB: + case CC_OP_SUBW: + case CC_OP_SUBL: + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); + /* Compute signed overflow for subtraction. */ + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_add_i32(t0, QREG_CC_N, QREG_CC_V); + gen_ext(t0, t0, s->cc_op - CC_OP_SUBB, 1); + tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V); + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0); + tcg_temp_free(t0); + tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, t1); + tcg_temp_free(t1); + break; + + case CC_OP_CMPB: + case CC_OP_CMPW: + case CC_OP_CMPL: + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_C, QREG_CC_N, QREG_CC_V); + tcg_gen_sub_i32(QREG_CC_Z, QREG_CC_N, QREG_CC_V); + gen_ext(QREG_CC_Z, QREG_CC_Z, s->cc_op - CC_OP_CMPB, 1); + /* Compute signed overflow for subtraction. */ + t0 = tcg_temp_new(); + tcg_gen_xor_i32(t0, QREG_CC_Z, QREG_CC_N); + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, QREG_CC_N); + tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, t0); + tcg_temp_free(t0); + tcg_gen_mov_i32(QREG_CC_N, QREG_CC_Z); + break; + + case CC_OP_LOGIC: + tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); + tcg_gen_movi_i32(QREG_CC_C, 0); + tcg_gen_movi_i32(QREG_CC_V, 0); + break; + + case CC_OP_DYNAMIC: + gen_helper_flush_flags(cpu_env, QREG_CC_OP); + break; + + default: + t0 = tcg_const_i32(s->cc_op); + gen_helper_flush_flags(cpu_env, t0); + tcg_temp_free(t0); + break; + } + + /* Note that flush_flags also assigned to env->cc_op. */ s->cc_op = CC_OP_FLAGS; + s->cc_op_synced = 1; +} + +static inline TCGv gen_extend(TCGv val, int opsize, int sign) +{ + TCGv tmp; + + if (opsize == OS_LONG) { + tmp = val; + } else { + tmp = tcg_temp_new(); + gen_ext(tmp, val, opsize, sign); + } + + return tmp; } -static void gen_logic_cc(DisasContext *s, TCGv val) +static void gen_logic_cc(DisasContext *s, TCGv val, int opsize) { - tcg_gen_mov_i32(QREG_CC_DEST, val); - s->cc_op = CC_OP_LOGIC; + gen_ext(QREG_CC_N, val, opsize, 1); + set_cc_op(s, CC_OP_LOGIC); } -static void gen_update_cc_add(TCGv dest, TCGv src) +static void gen_update_cc_cmp(DisasContext *s, TCGv dest, TCGv src, int opsize) { - tcg_gen_mov_i32(QREG_CC_DEST, dest); - tcg_gen_mov_i32(QREG_CC_SRC, src); + tcg_gen_mov_i32(QREG_CC_N, dest); + tcg_gen_mov_i32(QREG_CC_V, src); + set_cc_op(s, CC_OP_CMPB + opsize); +} + +static void gen_update_cc_add(TCGv dest, TCGv src, int opsize) +{ + gen_ext(QREG_CC_N, dest, opsize, 1); + tcg_gen_mov_i32(QREG_CC_V, src); } static inline int opsize_bytes(int opsize) @@ -438,6 +603,19 @@ static inline int opsize_bytes(int opsize) case OS_LONG: return 4; case OS_SINGLE: return 4; case OS_DOUBLE: return 8; + case OS_EXTENDED: return 12; + case OS_PACKED: return 12; + default: + g_assert_not_reached(); + } +} + +static inline int insn_opsize(int insn) +{ + switch ((insn >> 6) & 3) { + case 0: return OS_BYTE; + case 1: return OS_WORD; + case 2: return OS_LONG; default: g_assert_not_reached(); } @@ -470,36 +648,6 @@ static void gen_partset_reg(int opsize, TCGv reg, TCGv val) } } -/* Sign or zero extend a value. */ -static inline TCGv gen_extend(TCGv val, int opsize, int sign) -{ - TCGv tmp; - - switch (opsize) { - case OS_BYTE: - tmp = tcg_temp_new(); - if (sign) - tcg_gen_ext8s_i32(tmp, val); - else - tcg_gen_ext8u_i32(tmp, val); - break; - case OS_WORD: - tmp = tcg_temp_new(); - if (sign) - tcg_gen_ext16s_i32(tmp, val); - else - tcg_gen_ext16u_i32(tmp, val); - break; - case OS_LONG: - case OS_SINGLE: - tmp = val; - break; - default: - g_assert_not_reached(); - } - return tmp; -} - /* Generate code for an "effective address". Does not adjust the base register for autoincrement addressing modes. */ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn, @@ -525,8 +673,7 @@ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn, case 5: /* Indirect displacement. */ reg = AREG(insn, 0); tmp = tcg_temp_new(); - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); tcg_gen_addi_i32(tmp, reg, (int16_t)ext); return tmp; case 6: /* Indirect index + displacement. */ @@ -535,16 +682,14 @@ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn, case 7: /* Other */ switch (insn & 7) { case 0: /* Absolute short. */ - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; + offset = (int16_t)read_im16(env, s); return tcg_const_i32(offset); case 1: /* Absolute long. */ offset = read_im32(env, s); return tcg_const_i32(offset); case 2: /* pc displacement */ offset = s->pc; - offset += cpu_ldsw_code(env, s->pc); - s->pc += 2; + offset += (int16_t)read_im16(env, s); return tcg_const_i32(offset); case 3: /* pc index+displacement. */ return gen_lea_indexed(env, s, NULL_QREG); @@ -651,19 +796,17 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, switch (opsize) { case OS_BYTE: if (what == EA_LOADS) { - offset = cpu_ldsb_code(env, s->pc + 1); + offset = (int8_t)read_im8(env, s); } else { - offset = cpu_ldub_code(env, s->pc + 1); + offset = read_im8(env, s); } - s->pc += 2; break; case OS_WORD: if (what == EA_LOADS) { - offset = cpu_ldsw_code(env, s->pc); + offset = (int16_t)read_im16(env, s); } else { - offset = cpu_lduw_code(env, s->pc); + offset = read_im16(env, s); } - s->pc += 2; break; case OS_LONG: offset = read_im32(env, s); @@ -680,131 +823,217 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, return NULL_QREG; } -/* This generates a conditional branch, clobbering all temporaries. */ -static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) -{ - TCGv tmp; +typedef struct { + TCGCond tcond; + bool g1; + bool g2; + TCGv v1; + TCGv v2; +} DisasCompare; + +static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) +{ + TCGv tmp, tmp2; + TCGCond tcond; + CCOp op = s->cc_op; + + /* The CC_OP_CMP form can handle most normal comparisons directly. */ + if (op == CC_OP_CMPB || op == CC_OP_CMPW || op == CC_OP_CMPL) { + c->g1 = c->g2 = 1; + c->v1 = QREG_CC_N; + c->v2 = QREG_CC_V; + switch (cond) { + case 2: /* HI */ + case 3: /* LS */ + tcond = TCG_COND_LEU; + goto done; + case 4: /* CC */ + case 5: /* CS */ + tcond = TCG_COND_LTU; + goto done; + case 6: /* NE */ + case 7: /* EQ */ + tcond = TCG_COND_EQ; + goto done; + case 10: /* PL */ + case 11: /* MI */ + c->g1 = c->g2 = 0; + c->v2 = tcg_const_i32(0); + c->v1 = tmp = tcg_temp_new(); + tcg_gen_sub_i32(tmp, QREG_CC_N, QREG_CC_V); + gen_ext(tmp, tmp, op - CC_OP_CMPB, 1); + /* fallthru */ + case 12: /* GE */ + case 13: /* LT */ + tcond = TCG_COND_LT; + goto done; + case 14: /* GT */ + case 15: /* LE */ + tcond = TCG_COND_LE; + goto done; + } + } + + c->g1 = 1; + c->g2 = 0; + c->v2 = tcg_const_i32(0); - /* TODO: Optimize compare/branch pairs rather than always flushing - flag state to CC_OP_FLAGS. */ - gen_flush_flags(s); switch (cond) { case 0: /* T */ - tcg_gen_br(l1); - break; case 1: /* F */ + c->v1 = c->v2; + tcond = TCG_COND_NEVER; + goto done; + case 14: /* GT (!(Z || (N ^ V))) */ + case 15: /* LE (Z || (N ^ V)) */ + /* Logic operations clear V, which simplifies LE to (Z || N), + and since Z and N are co-located, this becomes a normal + comparison vs N. */ + if (op == CC_OP_LOGIC) { + c->v1 = QREG_CC_N; + tcond = TCG_COND_LE; + goto done; + } break; - case 2: /* HI (!C && !Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + case 12: /* GE (!(N ^ V)) */ + case 13: /* LT (N ^ V) */ + /* Logic operations clear V, which simplifies this to N. */ + if (op != CC_OP_LOGIC) { + break; + } + /* fallthru */ + case 10: /* PL (!N) */ + case 11: /* MI (N) */ + /* Several cases represent N normally. */ + if (op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL || + op == CC_OP_SUBB || op == CC_OP_SUBW || op == CC_OP_SUBL || + op == CC_OP_LOGIC) { + c->v1 = QREG_CC_N; + tcond = TCG_COND_LT; + goto done; + } break; - case 3: /* LS (C || Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + case 6: /* NE (!Z) */ + case 7: /* EQ (Z) */ + /* Some cases fold Z into N. */ + if (op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL || + op == CC_OP_SUBB || op == CC_OP_SUBW || op == CC_OP_SUBL || + op == CC_OP_LOGIC) { + tcond = TCG_COND_EQ; + c->v1 = QREG_CC_N; + goto done; + } break; case 4: /* CC (!C) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + case 5: /* CS (C) */ + /* Some cases fold C into X. */ + if (op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL || + op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL) { + tcond = TCG_COND_NE; + c->v1 = QREG_CC_X; + goto done; + } + /* fallthru */ + case 8: /* VC (!V) */ + case 9: /* VS (V) */ + /* Logic operations clear V and C. */ + if (op == CC_OP_LOGIC) { + tcond = TCG_COND_NEVER; + c->v1 = c->v2; + goto done; + } + break; + } + + /* Otherwise, flush flag state to CC_OP_FLAGS. */ + gen_flush_flags(s); + + switch (cond) { + case 0: /* T */ + case 1: /* F */ + default: + /* Invalid, or handled above. */ + abort(); + case 2: /* HI (!C && !Z) -> !(C || Z)*/ + case 3: /* LS (C || Z) */ + c->v1 = tmp = tcg_temp_new(); + c->g1 = 0; + tcg_gen_setcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); + tcg_gen_or_i32(tmp, tmp, QREG_CC_C); + tcond = TCG_COND_NE; break; + case 4: /* CC (!C) */ case 5: /* CS (C) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_C; + tcond = TCG_COND_NE; break; case 6: /* NE (!Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 7: /* EQ (Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_Z; + tcond = TCG_COND_EQ; break; case 8: /* VC (!V) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 9: /* VS (V) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_V; + tcond = TCG_COND_LT; break; case 10: /* PL (!N) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 11: /* MI (N) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_N; + tcond = TCG_COND_LT; break; case 12: /* GE (!(N ^ V)) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 13: /* LT (N ^ V) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = tmp = tcg_temp_new(); + c->g1 = 0; + tcg_gen_xor_i32(tmp, QREG_CC_N, QREG_CC_V); + tcond = TCG_COND_LT; break; case 14: /* GT (!(Z || (N ^ V))) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_shri_i32(tmp, tmp, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 15: /* LE (Z || (N ^ V)) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_shri_i32(tmp, tmp, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = tmp = tcg_temp_new(); + c->g1 = 0; + tcg_gen_setcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); + tcg_gen_neg_i32(tmp, tmp); + tmp2 = tcg_temp_new(); + tcg_gen_xor_i32(tmp2, QREG_CC_N, QREG_CC_V); + tcg_gen_or_i32(tmp, tmp, tmp2); + tcg_temp_free(tmp2); + tcond = TCG_COND_LT; break; - default: - /* Should ever happen. */ - abort(); } + + done: + if ((cond & 1) == 0) { + tcond = tcg_invert_cond(tcond); + } + c->tcond = tcond; } -DISAS_INSN(scc) +static void free_cond(DisasCompare *c) { - TCGLabel *l1; - int cond; - TCGv reg; + if (!c->g1) { + tcg_temp_free(c->v1); + } + if (!c->g2) { + tcg_temp_free(c->v2); + } +} - l1 = gen_new_label(); - cond = (insn >> 8) & 0xf; - reg = DREG(insn, 0); - tcg_gen_andi_i32(reg, reg, 0xffffff00); - /* This is safe because we modify the reg directly, with no other values - live. */ - gen_jmpcc(s, cond ^ 1, l1); - tcg_gen_ori_i32(reg, reg, 0xff); - gen_set_label(l1); +static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) +{ + DisasCompare c; + + gen_cc_cond(&c, s, cond); + update_cc_op(s); + tcg_gen_brcond_i32(c.tcond, c.v1, c.v2, l1); + free_cond(&c); } /* Force a TB lookup after an instruction that changes the CPU state. */ static void gen_lookup_tb(DisasContext *s) { - gen_flush_cc_op(s); + update_cc_op(s); tcg_gen_movi_i32(QREG_PC, s->pc); s->is_jmp = DISAS_UPDATE; } @@ -812,7 +1041,7 @@ static void gen_lookup_tb(DisasContext *s) /* Generate a jump to an immediate address. */ static void gen_jmp_im(DisasContext *s, uint32_t dest) { - gen_flush_cc_op(s); + update_cc_op(s); tcg_gen_movi_i32(QREG_PC, dest); s->is_jmp = DISAS_JUMP; } @@ -820,14 +1049,14 @@ static void gen_jmp_im(DisasContext *s, uint32_t dest) /* Generate a jump to the address in qreg DEST. */ static void gen_jmp(DisasContext *s, TCGv dest) { - gen_flush_cc_op(s); + update_cc_op(s); tcg_gen_mov_i32(QREG_PC, dest); s->is_jmp = DISAS_JUMP; } static void gen_exception(DisasContext *s, uint32_t where, int nr) { - gen_flush_cc_op(s); + update_cc_op(s); gen_jmp_im(s, where); gen_helper_raise_exception(cpu_env, tcg_const_i32(nr)); } @@ -880,6 +1109,48 @@ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) s->is_jmp = DISAS_TB_JUMP; } +DISAS_INSN(scc) +{ + DisasCompare c; + int cond; + TCGv tmp; + + cond = (insn >> 8) & 0xf; + gen_cc_cond(&c, s, cond); + + tmp = tcg_temp_new(); + tcg_gen_setcond_i32(c.tcond, tmp, c.v1, c.v2); + free_cond(&c); + + tcg_gen_neg_i32(tmp, tmp); + DEST_EA(env, insn, OS_BYTE, tmp, NULL); + tcg_temp_free(tmp); +} + +DISAS_INSN(dbcc) +{ + TCGLabel *l1; + TCGv reg; + TCGv tmp; + int16_t offset; + uint32_t base; + + reg = DREG(insn, 0); + base = s->pc; + offset = (int16_t)read_im16(env, s); + l1 = gen_new_label(); + gen_jmpcc(s, (insn >> 8) & 0xf, l1); + + tmp = tcg_temp_new(); + tcg_gen_ext16s_i32(tmp, reg); + tcg_gen_addi_i32(tmp, tmp, -1); + gen_partset_reg(OS_WORD, reg, tmp); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, -1, l1); + gen_jmp_tb(s, 1, base + offset); + gen_set_label(l1); + gen_jmp_tb(s, 0, s->pc); +} + DISAS_INSN(undef_mac) { gen_exception(s, s->pc - 2, EXCP_LINEA); @@ -915,8 +1186,7 @@ DISAS_INSN(mulw) SRC_EA(env, src, OS_WORD, sign, NULL); tcg_gen_mul_i32(tmp, tmp, src); tcg_gen_mov_i32(reg, tmp); - /* Unlike m68k, coldfire always clears the overflow bit. */ - gen_logic_cc(s, tmp); + gen_logic_cc(s, tmp, OS_WORD); } DISAS_INSN(divw) @@ -946,7 +1216,8 @@ DISAS_INSN(divw) tcg_gen_ext16u_i32(tmp, QREG_DIV1); tcg_gen_shli_i32(src, QREG_DIV2, 16); tcg_gen_or_i32(reg, tmp, src); - s->cc_op = CC_OP_FLAGS; + + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(divl) @@ -956,8 +1227,7 @@ DISAS_INSN(divl) TCGv reg; uint16_t ext; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext & 0x87f8) { gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED); return; @@ -979,7 +1249,7 @@ DISAS_INSN(divl) /* rem */ tcg_gen_mov_i32 (reg, QREG_DIV2); } - s->cc_op = CC_OP_FLAGS; + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(addsub) @@ -990,35 +1260,37 @@ DISAS_INSN(addsub) TCGv tmp; TCGv addr; int add; + int opsize; add = (insn & 0x4000) != 0; - reg = DREG(insn, 9); + opsize = insn_opsize(insn); + reg = gen_extend(DREG(insn, 9), opsize, 1); dest = tcg_temp_new(); if (insn & 0x100) { - SRC_EA(env, tmp, OS_LONG, 0, &addr); + SRC_EA(env, tmp, opsize, 1, &addr); src = reg; } else { tmp = reg; - SRC_EA(env, src, OS_LONG, 0, NULL); + SRC_EA(env, src, opsize, 1, NULL); } if (add) { tcg_gen_add_i32(dest, tmp, src); - gen_helper_xflag_lt(QREG_CC_X, dest, src); - s->cc_op = CC_OP_ADD; + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src); + set_cc_op(s, CC_OP_ADDB + opsize); } else { - gen_helper_xflag_lt(QREG_CC_X, tmp, src); + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, tmp, src); tcg_gen_sub_i32(dest, tmp, src); - s->cc_op = CC_OP_SUB; + set_cc_op(s, CC_OP_SUBB + opsize); } - gen_update_cc_add(dest, src); + gen_update_cc_add(dest, src, opsize); if (insn & 0x100) { - DEST_EA(env, insn, OS_LONG, dest, &addr); + DEST_EA(env, insn, opsize, dest, &addr); } else { - tcg_gen_mov_i32(reg, dest); + gen_partset_reg(opsize, DREG(insn, 9), dest); } + tcg_temp_free(dest); } - /* Reverse the order of the bits in REG. */ DISAS_INSN(bitrev) { @@ -1043,32 +1315,27 @@ DISAS_INSN(bitop_reg) opsize = OS_LONG; op = (insn >> 6) & 3; SRC_EA(env, src1, opsize, 0, op ? &addr: NULL); - src2 = DREG(insn, 9); - dest = tcg_temp_new(); gen_flush_flags(s); - tmp = tcg_temp_new(); + src2 = tcg_temp_new(); if (opsize == OS_BYTE) - tcg_gen_andi_i32(tmp, src2, 7); + tcg_gen_andi_i32(src2, DREG(insn, 9), 7); else - tcg_gen_andi_i32(tmp, src2, 31); - src2 = tmp; - tmp = tcg_temp_new(); - tcg_gen_shr_i32(tmp, src1, src2); - tcg_gen_andi_i32(tmp, tmp, 1); - tcg_gen_shli_i32(tmp, tmp, 2); - /* Clear CCF_Z if bit set. */ - tcg_gen_ori_i32(QREG_CC_DEST, QREG_CC_DEST, CCF_Z); - tcg_gen_xor_i32(QREG_CC_DEST, QREG_CC_DEST, tmp); - - tcg_gen_shl_i32(tmp, tcg_const_i32(1), src2); + tcg_gen_andi_i32(src2, DREG(insn, 9), 31); + + tmp = tcg_const_i32(1); + tcg_gen_shl_i32(tmp, tmp, src2); + tcg_temp_free(src2); + + tcg_gen_and_i32(QREG_CC_Z, src1, tmp); + + dest = tcg_temp_new(); switch (op) { case 1: /* bchg */ tcg_gen_xor_i32(dest, src1, tmp); break; case 2: /* bclr */ - tcg_gen_not_i32(tmp, tmp); - tcg_gen_and_i32(dest, src1, tmp); + tcg_gen_andc_i32(dest, src1, tmp); break; case 3: /* bset */ tcg_gen_or_i32(dest, src1, tmp); @@ -1076,8 +1343,11 @@ DISAS_INSN(bitop_reg) default: /* btst */ break; } - if (op) + tcg_temp_free(tmp); + if (op) { DEST_EA(env, insn, opsize, dest, &addr); + } + tcg_temp_free(dest); } DISAS_INSN(sats) @@ -1085,8 +1355,8 @@ DISAS_INSN(sats) TCGv reg; reg = DREG(insn, 0); gen_flush_flags(s); - gen_helper_sats(reg, reg, QREG_CC_DEST); - gen_logic_cc(s, reg); + gen_helper_sats(reg, reg, QREG_CC_V); + gen_logic_cc(s, reg, OS_LONG); } static void gen_push(DisasContext *s, TCGv val) @@ -1108,8 +1378,7 @@ DISAS_INSN(movem) TCGv tmp; int is_load; - mask = cpu_lduw_code(env, s->pc); - s->pc += 2; + mask = read_im16(env, s); tmp = gen_lea(env, s, insn, OS_LONG); if (IS_NULL_QREG(tmp)) { gen_addr_fault(s); @@ -1152,8 +1421,7 @@ DISAS_INSN(bitop_im) opsize = OS_LONG; op = (insn >> 6) & 3; - bitnum = cpu_lduw_code(env, s->pc); - s->pc += 2; + bitnum = read_im16(env, s); if (bitnum & 0xff00) { disas_undef(env, s, insn); return; @@ -1168,19 +1436,10 @@ DISAS_INSN(bitop_im) bitnum &= 31; mask = 1 << bitnum; - tmp = tcg_temp_new(); - assert (CCF_Z == (1 << 2)); - if (bitnum > 2) - tcg_gen_shri_i32(tmp, src1, bitnum - 2); - else if (bitnum < 2) - tcg_gen_shli_i32(tmp, src1, 2 - bitnum); - else - tcg_gen_mov_i32(tmp, src1); - tcg_gen_andi_i32(tmp, tmp, CCF_Z); - /* Clear CCF_Z if bit set. */ - tcg_gen_ori_i32(QREG_CC_DEST, QREG_CC_DEST, CCF_Z); - tcg_gen_xor_i32(QREG_CC_DEST, QREG_CC_DEST, tmp); + tcg_gen_andi_i32(QREG_CC_Z, src1, mask); + if (op) { + tmp = tcg_temp_new(); switch (op) { case 1: /* bchg */ tcg_gen_xori_i32(tmp, src1, mask); @@ -1195,60 +1454,72 @@ DISAS_INSN(bitop_im) break; } DEST_EA(env, insn, opsize, tmp, &addr); + tcg_temp_free(tmp); } } DISAS_INSN(arith_im) { int op; - uint32_t im; + TCGv im; TCGv src1; TCGv dest; TCGv addr; + int opsize; op = (insn >> 9) & 7; - SRC_EA(env, src1, OS_LONG, 0, (op == 6) ? NULL : &addr); - im = read_im32(env, s); + opsize = insn_opsize(insn); + switch (opsize) { + case OS_BYTE: + im = tcg_const_i32((int8_t)read_im8(env, s)); + break; + case OS_WORD: + im = tcg_const_i32((int16_t)read_im16(env, s)); + break; + case OS_LONG: + im = tcg_const_i32(read_im32(env, s)); + break; + default: + abort(); + } + SRC_EA(env, src1, opsize, 1, (op == 6) ? NULL : &addr); dest = tcg_temp_new(); switch (op) { case 0: /* ori */ - tcg_gen_ori_i32(dest, src1, im); - gen_logic_cc(s, dest); + tcg_gen_or_i32(dest, src1, im); + gen_logic_cc(s, dest, opsize); break; case 1: /* andi */ - tcg_gen_andi_i32(dest, src1, im); - gen_logic_cc(s, dest); + tcg_gen_and_i32(dest, src1, im); + gen_logic_cc(s, dest, opsize); break; case 2: /* subi */ - tcg_gen_mov_i32(dest, src1); - gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im)); - tcg_gen_subi_i32(dest, dest, im); - gen_update_cc_add(dest, tcg_const_i32(im)); - s->cc_op = CC_OP_SUB; + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, src1, im); + tcg_gen_sub_i32(dest, src1, im); + gen_update_cc_add(dest, im, opsize); + set_cc_op(s, CC_OP_SUBB + opsize); break; case 3: /* addi */ - tcg_gen_mov_i32(dest, src1); - tcg_gen_addi_i32(dest, dest, im); - gen_update_cc_add(dest, tcg_const_i32(im)); - gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im)); - s->cc_op = CC_OP_ADD; + tcg_gen_add_i32(dest, src1, im); + gen_update_cc_add(dest, im, opsize); + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, im); + set_cc_op(s, CC_OP_ADDB + opsize); break; case 5: /* eori */ - tcg_gen_xori_i32(dest, src1, im); - gen_logic_cc(s, dest); + tcg_gen_xor_i32(dest, src1, im); + gen_logic_cc(s, dest, opsize); break; case 6: /* cmpi */ - tcg_gen_mov_i32(dest, src1); - tcg_gen_subi_i32(dest, dest, im); - gen_update_cc_add(dest, tcg_const_i32(im)); - s->cc_op = CC_OP_SUB; + gen_update_cc_cmp(s, src1, im, opsize); break; default: abort(); } + tcg_temp_free(im); if (op != 6) { - DEST_EA(env, insn, OS_LONG, dest, &addr); + DEST_EA(env, insn, opsize, dest, &addr); } + tcg_temp_free(dest); } DISAS_INSN(byterev) @@ -1292,17 +1563,50 @@ DISAS_INSN(move) dest_ea = ((insn >> 9) & 7) | (op << 3); DEST_EA(env, dest_ea, opsize, src, NULL); /* This will be correct because loads sign extend. */ - gen_logic_cc(s, src); + gen_logic_cc(s, src, opsize); } } DISAS_INSN(negx) { - TCGv reg; + TCGv z; + TCGv src; + TCGv addr; + int opsize; - gen_flush_flags(s); - reg = DREG(insn, 0); - gen_helper_subx_cc(reg, cpu_env, tcg_const_i32(0), reg); + opsize = insn_opsize(insn); + SRC_EA(env, src, opsize, 1, &addr); + + gen_flush_flags(s); /* compute old Z */ + + /* Perform substract with borrow. + * (X, N) = -(src + X); + */ + + z = tcg_const_i32(0); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, z, QREG_CC_X, z); + tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, z, z, QREG_CC_N, QREG_CC_X); + tcg_temp_free(z); + gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); + + tcg_gen_andi_i32(QREG_CC_X, QREG_CC_X, 1); + + /* Compute signed-overflow for negation. The normal formula for + * subtraction is (res ^ src) & (src ^ dest), but with dest==0 + * this simplies to res & src. + */ + + tcg_gen_and_i32(QREG_CC_V, QREG_CC_N, src); + + /* Copy the rest of the results into place. */ + tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */ + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + + set_cc_op(s, CC_OP_FLAGS); + + /* result is in QREG_CC_N */ + + DEST_EA(env, insn, opsize, QREG_CC_N, &addr); } DISAS_INSN(lea) @@ -1323,21 +1627,9 @@ DISAS_INSN(clr) { int opsize; - switch ((insn >> 6) & 3) { - case 0: /* clr.b */ - opsize = OS_BYTE; - break; - case 1: /* clr.w */ - opsize = OS_WORD; - break; - case 2: /* clr.l */ - opsize = OS_LONG; - break; - default: - abort(); - } + opsize = insn_opsize(insn); DEST_EA(env, insn, opsize, tcg_const_i32(0), NULL); - gen_logic_cc(s, tcg_const_i32(0)); + gen_logic_cc(s, tcg_const_i32(0), opsize); } static TCGv gen_get_ccr(DisasContext *s) @@ -1345,75 +1637,72 @@ static TCGv gen_get_ccr(DisasContext *s) TCGv dest; gen_flush_flags(s); + update_cc_op(s); dest = tcg_temp_new(); - tcg_gen_shli_i32(dest, QREG_CC_X, 4); - tcg_gen_or_i32(dest, dest, QREG_CC_DEST); + gen_helper_get_ccr(dest, cpu_env); return dest; } DISAS_INSN(move_from_ccr) { - TCGv reg; TCGv ccr; ccr = gen_get_ccr(s); - reg = DREG(insn, 0); - gen_partset_reg(OS_WORD, reg, ccr); + DEST_EA(env, insn, OS_WORD, ccr, NULL); } DISAS_INSN(neg) { - TCGv reg; TCGv src1; + TCGv dest; + TCGv addr; + int opsize; - reg = DREG(insn, 0); - src1 = tcg_temp_new(); - tcg_gen_mov_i32(src1, reg); - tcg_gen_neg_i32(reg, src1); - s->cc_op = CC_OP_SUB; - gen_update_cc_add(reg, src1); - gen_helper_xflag_lt(QREG_CC_X, tcg_const_i32(0), src1); - s->cc_op = CC_OP_SUB; + opsize = insn_opsize(insn); + SRC_EA(env, src1, opsize, 1, &addr); + dest = tcg_temp_new(); + tcg_gen_neg_i32(dest, src1); + set_cc_op(s, CC_OP_SUBB + opsize); + gen_update_cc_add(dest, src1, opsize); + tcg_gen_setcondi_i32(TCG_COND_NE, QREG_CC_X, dest, 0); + DEST_EA(env, insn, opsize, dest, &addr); + tcg_temp_free(dest); } static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) { - tcg_gen_movi_i32(QREG_CC_DEST, val & 0xf); - tcg_gen_movi_i32(QREG_CC_X, (val & 0x10) >> 4); - if (!ccr_only) { - gen_helper_set_sr(cpu_env, tcg_const_i32(val & 0xff00)); + if (ccr_only) { + tcg_gen_movi_i32(QREG_CC_C, val & CCF_C ? 1 : 0); + tcg_gen_movi_i32(QREG_CC_V, val & CCF_V ? -1 : 0); + tcg_gen_movi_i32(QREG_CC_Z, val & CCF_Z ? 0 : 1); + tcg_gen_movi_i32(QREG_CC_N, val & CCF_N ? -1 : 0); + tcg_gen_movi_i32(QREG_CC_X, val & CCF_X ? 1 : 0); + } else { + gen_helper_set_sr(cpu_env, tcg_const_i32(val)); } + set_cc_op(s, CC_OP_FLAGS); } static void gen_set_sr(CPUM68KState *env, DisasContext *s, uint16_t insn, int ccr_only) { - TCGv tmp; - TCGv reg; - - s->cc_op = CC_OP_FLAGS; - if ((insn & 0x38) == 0) - { - tmp = tcg_temp_new(); - reg = DREG(insn, 0); - tcg_gen_andi_i32(QREG_CC_DEST, reg, 0xf); - tcg_gen_shri_i32(tmp, reg, 4); - tcg_gen_andi_i32(QREG_CC_X, tmp, 1); - if (!ccr_only) { - gen_helper_set_sr(cpu_env, reg); + if ((insn & 0x38) == 0) { + if (ccr_only) { + gen_helper_set_ccr(cpu_env, DREG(insn, 0)); + } else { + gen_helper_set_sr(cpu_env, DREG(insn, 0)); } - } - else if ((insn & 0x3f) == 0x3c) - { + set_cc_op(s, CC_OP_FLAGS); + } else if ((insn & 0x3f) == 0x3c) { uint16_t val; - val = cpu_lduw_code(env, s->pc); - s->pc += 2; + val = read_im16(env, s); gen_set_sr_im(s, val, ccr_only); - } - else + } else { disas_undef(env, s, insn); + } } + DISAS_INSN(move_to_ccr) { gen_set_sr(env, s, insn, 1); @@ -1421,11 +1710,17 @@ DISAS_INSN(move_to_ccr) DISAS_INSN(not) { - TCGv reg; + TCGv src1; + TCGv dest; + TCGv addr; + int opsize; - reg = DREG(insn, 0); - tcg_gen_not_i32(reg, reg); - gen_logic_cc(s, reg); + opsize = insn_opsize(insn); + SRC_EA(env, src1, opsize, 1, &addr); + dest = tcg_temp_new(); + tcg_gen_not_i32(dest, src1); + DEST_EA(env, insn, opsize, dest, &addr); + gen_logic_cc(s, dest, opsize); } DISAS_INSN(swap) @@ -1440,7 +1735,12 @@ DISAS_INSN(swap) tcg_gen_shli_i32(src1, reg, 16); tcg_gen_shri_i32(src2, reg, 16); tcg_gen_or_i32(reg, src1, src2); - gen_logic_cc(s, reg); + gen_logic_cc(s, reg, OS_LONG); +} + +DISAS_INSN(bkpt) +{ + gen_exception(s, s->pc - 2, EXCP_DEBUG); } DISAS_INSN(pea) @@ -1472,7 +1772,7 @@ DISAS_INSN(ext) gen_partset_reg(OS_WORD, reg, tmp); else tcg_gen_mov_i32(reg, tmp); - gen_logic_cc(s, tmp); + gen_logic_cc(s, tmp, OS_LONG); } DISAS_INSN(tst) @@ -1480,21 +1780,9 @@ DISAS_INSN(tst) int opsize; TCGv tmp; - switch ((insn >> 6) & 3) { - case 0: /* tst.b */ - opsize = OS_BYTE; - break; - case 1: /* tst.w */ - opsize = OS_WORD; - break; - case 2: /* tst.l */ - opsize = OS_LONG; - break; - default: - abort(); - } + opsize = insn_opsize(insn); SRC_EA(env, tmp, opsize, 1, NULL); - gen_logic_cc(s, tmp); + gen_logic_cc(s, tmp, opsize); } DISAS_INSN(pulse) @@ -1516,7 +1804,7 @@ DISAS_INSN(tas) dest = tcg_temp_new(); SRC_EA(env, src1, OS_BYTE, 1, &addr); - gen_logic_cc(s, src1); + gen_logic_cc(s, src1, OS_BYTE); tcg_gen_ori_i32(dest, src1, 0x80); DEST_EA(env, insn, OS_BYTE, dest, &addr); } @@ -1530,8 +1818,7 @@ DISAS_INSN(mull) /* The upper 32 bits of the product are discarded, so muls.l and mulu.l are functionally equivalent. */ - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext & 0x87ff) { gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED); return; @@ -1542,24 +1829,39 @@ DISAS_INSN(mull) tcg_gen_mul_i32(dest, src1, reg); tcg_gen_mov_i32(reg, dest); /* Unlike m68k, coldfire always clears the overflow bit. */ - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); } -DISAS_INSN(link) +static void gen_link(DisasContext *s, uint16_t insn, int32_t offset) { - int16_t offset; TCGv reg; TCGv tmp; - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; reg = AREG(insn, 0); tmp = tcg_temp_new(); tcg_gen_subi_i32(tmp, QREG_SP, 4); gen_store(s, OS_LONG, tmp, reg); - if ((insn & 7) != 7) + if ((insn & 7) != 7) { tcg_gen_mov_i32(reg, tmp); + } tcg_gen_addi_i32(QREG_SP, tmp, offset); + tcg_temp_free(tmp); +} + +DISAS_INSN(link) +{ + int16_t offset; + + offset = read_im16(env, s); + gen_link(s, insn, offset); +} + +DISAS_INSN(linkl) +{ + int32_t offset; + + offset = read_im32(env, s); + gen_link(s, insn, offset); } DISAS_INSN(unlk) @@ -1609,40 +1911,48 @@ DISAS_INSN(jump) DISAS_INSN(addsubq) { - TCGv src1; - TCGv src2; + TCGv src; TCGv dest; - int val; + TCGv val; + int imm; TCGv addr; + int opsize; - SRC_EA(env, src1, OS_LONG, 0, &addr); - val = (insn >> 9) & 7; - if (val == 0) - val = 8; + if ((insn & 070) == 010) { + /* Operation on address register is always long. */ + opsize = OS_LONG; + } else { + opsize = insn_opsize(insn); + } + SRC_EA(env, src, opsize, 1, &addr); + imm = (insn >> 9) & 7; + if (imm == 0) { + imm = 8; + } + val = tcg_const_i32(imm); dest = tcg_temp_new(); - tcg_gen_mov_i32(dest, src1); + tcg_gen_mov_i32(dest, src); if ((insn & 0x38) == 0x08) { /* Don't update condition codes if the destination is an address register. */ if (insn & 0x0100) { - tcg_gen_subi_i32(dest, dest, val); + tcg_gen_sub_i32(dest, dest, val); } else { - tcg_gen_addi_i32(dest, dest, val); + tcg_gen_add_i32(dest, dest, val); } } else { - src2 = tcg_const_i32(val); if (insn & 0x0100) { - gen_helper_xflag_lt(QREG_CC_X, dest, src2); - tcg_gen_subi_i32(dest, dest, val); - s->cc_op = CC_OP_SUB; + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, val); + tcg_gen_sub_i32(dest, dest, val); + set_cc_op(s, CC_OP_SUBB + opsize); } else { - tcg_gen_addi_i32(dest, dest, val); - gen_helper_xflag_lt(QREG_CC_X, dest, src2); - s->cc_op = CC_OP_ADD; + tcg_gen_add_i32(dest, dest, val); + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, val); + set_cc_op(s, CC_OP_ADDB + opsize); } - gen_update_cc_add(dest, src2); + gen_update_cc_add(dest, val, opsize); } - DEST_EA(env, insn, OS_LONG, dest, &addr); + DEST_EA(env, insn, opsize, dest, &addr); } DISAS_INSN(tpf) @@ -1672,8 +1982,7 @@ DISAS_INSN(branch) op = (insn >> 8) & 0xf; offset = (int8_t)insn; if (offset == 0) { - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; + offset = (int16_t)read_im16(env, s); } else if (offset == -1) { offset = read_im32(env, s); } @@ -1681,7 +1990,6 @@ DISAS_INSN(branch) /* bsr */ gen_push(s, tcg_const_i32(s->pc)); } - gen_flush_cc_op(s); if (op > 1) { /* Bcc */ l1 = gen_new_label(); @@ -1701,7 +2009,7 @@ DISAS_INSN(moveq) val = (int8_t)insn; tcg_gen_movi_i32(DREG(insn, 9), val); - gen_logic_cc(s, tcg_const_i32(val)); + gen_logic_cc(s, tcg_const_i32(val), OS_LONG); } DISAS_INSN(mvzs) @@ -1717,7 +2025,7 @@ DISAS_INSN(mvzs) SRC_EA(env, src, opsize, (insn & 0x80) == 0, NULL); reg = DREG(insn, 9); tcg_gen_mov_i32(reg, src); - gen_logic_cc(s, src); + gen_logic_cc(s, src, opsize); } DISAS_INSN(or) @@ -1726,19 +2034,21 @@ DISAS_INSN(or) TCGv dest; TCGv src; TCGv addr; + int opsize; - reg = DREG(insn, 9); + opsize = insn_opsize(insn); + reg = gen_extend(DREG(insn, 9), opsize, 0); dest = tcg_temp_new(); if (insn & 0x100) { - SRC_EA(env, src, OS_LONG, 0, &addr); + SRC_EA(env, src, opsize, 0, &addr); tcg_gen_or_i32(dest, src, reg); - DEST_EA(env, insn, OS_LONG, dest, &addr); + DEST_EA(env, insn, opsize, dest, &addr); } else { - SRC_EA(env, src, OS_LONG, 0, NULL); + SRC_EA(env, src, opsize, 0, NULL); tcg_gen_or_i32(dest, src, reg); - tcg_gen_mov_i32(reg, dest); + gen_partset_reg(opsize, DREG(insn, 9), dest); } - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, opsize); } DISAS_INSN(suba) @@ -1746,20 +2056,80 @@ DISAS_INSN(suba) TCGv src; TCGv reg; - SRC_EA(env, src, OS_LONG, 0, NULL); + SRC_EA(env, src, (insn & 0x100) ? OS_LONG : OS_WORD, 1, NULL); reg = AREG(insn, 9); tcg_gen_sub_i32(reg, reg, src); } -DISAS_INSN(subx) +static inline void gen_subx(DisasContext *s, TCGv src, TCGv dest, int opsize) { - TCGv reg; + TCGv tmp; + + gen_flush_flags(s); /* compute old Z */ + + /* Perform substract with borrow. + * (X, N) = dest - (src + X); + */ + + tmp = tcg_const_i32(0); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, tmp, QREG_CC_X, tmp); + tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, dest, tmp, QREG_CC_N, QREG_CC_X); + gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); + tcg_gen_andi_i32(QREG_CC_X, QREG_CC_X, 1); + + /* Compute signed-overflow for substract. */ + + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, dest); + tcg_gen_xor_i32(tmp, dest, src); + tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, tmp); + tcg_temp_free(tmp); + + /* Copy the rest of the results into place. */ + tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */ + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + + set_cc_op(s, CC_OP_FLAGS); + + /* result is in QREG_CC_N */ +} + +DISAS_INSN(subx_reg) +{ + TCGv dest; TCGv src; + int opsize; - gen_flush_flags(s); - reg = DREG(insn, 9); - src = DREG(insn, 0); - gen_helper_subx_cc(reg, cpu_env, reg, src); + opsize = insn_opsize(insn); + + src = gen_extend(DREG(insn, 0), opsize, 1); + dest = gen_extend(DREG(insn, 9), opsize, 1); + + gen_subx(s, src, dest, opsize); + + gen_partset_reg(opsize, DREG(insn, 9), QREG_CC_N); +} + +DISAS_INSN(subx_mem) +{ + TCGv src; + TCGv addr_src; + TCGv dest; + TCGv addr_dest; + int opsize; + + opsize = insn_opsize(insn); + + addr_src = AREG(insn, 0); + tcg_gen_subi_i32(addr_src, addr_src, opsize); + src = gen_load(s, opsize, addr_src, 1); + + addr_dest = AREG(insn, 9); + tcg_gen_subi_i32(addr_dest, addr_dest, opsize); + dest = gen_load(s, opsize, addr_dest, 1); + + gen_subx(s, src, dest, opsize); + + gen_store(s, opsize, addr_dest, QREG_CC_N); } DISAS_INSN(mov3q) @@ -1771,40 +2141,20 @@ DISAS_INSN(mov3q) if (val == 0) val = -1; src = tcg_const_i32(val); - gen_logic_cc(s, src); + gen_logic_cc(s, src, OS_LONG); DEST_EA(env, insn, OS_LONG, src, NULL); } DISAS_INSN(cmp) { - int op; TCGv src; TCGv reg; - TCGv dest; int opsize; - op = (insn >> 6) & 3; - switch (op) { - case 0: /* cmp.b */ - opsize = OS_BYTE; - s->cc_op = CC_OP_CMPB; - break; - case 1: /* cmp.w */ - opsize = OS_WORD; - s->cc_op = CC_OP_CMPW; - break; - case 2: /* cmp.l */ - opsize = OS_LONG; - s->cc_op = CC_OP_SUB; - break; - default: - abort(); - } + opsize = insn_opsize(insn); SRC_EA(env, src, opsize, 1, NULL); - reg = DREG(insn, 9); - dest = tcg_temp_new(); - tcg_gen_sub_i32(dest, reg, src); - gen_update_cc_add(dest, src); + reg = gen_extend(DREG(insn, 9), opsize, 1); + gen_update_cc_cmp(s, reg, src, opsize); } DISAS_INSN(cmpa) @@ -1812,7 +2162,6 @@ DISAS_INSN(cmpa) int opsize; TCGv src; TCGv reg; - TCGv dest; if (insn & 0x100) { opsize = OS_LONG; @@ -1821,25 +2170,50 @@ DISAS_INSN(cmpa) } SRC_EA(env, src, opsize, 1, NULL); reg = AREG(insn, 9); - dest = tcg_temp_new(); - tcg_gen_sub_i32(dest, reg, src); - gen_update_cc_add(dest, src); - s->cc_op = CC_OP_SUB; + gen_update_cc_cmp(s, reg, src, opsize); } DISAS_INSN(eor) { TCGv src; - TCGv reg; TCGv dest; TCGv addr; + int opsize; - SRC_EA(env, src, OS_LONG, 0, &addr); - reg = DREG(insn, 9); + opsize = insn_opsize(insn); + + SRC_EA(env, src, opsize, 0, &addr); dest = tcg_temp_new(); - tcg_gen_xor_i32(dest, src, reg); - gen_logic_cc(s, dest); - DEST_EA(env, insn, OS_LONG, dest, &addr); + tcg_gen_xor_i32(dest, src, DREG(insn, 9)); + gen_logic_cc(s, dest, opsize); + DEST_EA(env, insn, opsize, dest, &addr); +} + +static void do_exg(TCGv reg1, TCGv reg2) +{ + TCGv temp = tcg_temp_new(); + tcg_gen_mov_i32(temp, reg1); + tcg_gen_mov_i32(reg1, reg2); + tcg_gen_mov_i32(reg2, temp); + tcg_temp_free(temp); +} + +DISAS_INSN(exg_aa) +{ + /* exchange Dx and Dy */ + do_exg(DREG(insn, 9), DREG(insn, 0)); +} + +DISAS_INSN(exg_dd) +{ + /* exchange Ax and Ay */ + do_exg(AREG(insn, 9), AREG(insn, 0)); +} + +DISAS_INSN(exg_da) +{ + /* exchange Dx and Ay */ + do_exg(DREG(insn, 9), AREG(insn, 0)); } DISAS_INSN(and) @@ -1848,19 +2222,23 @@ DISAS_INSN(and) TCGv reg; TCGv dest; TCGv addr; + int opsize; - reg = DREG(insn, 9); dest = tcg_temp_new(); + + opsize = insn_opsize(insn); + reg = DREG(insn, 9); if (insn & 0x100) { - SRC_EA(env, src, OS_LONG, 0, &addr); + SRC_EA(env, src, opsize, 0, &addr); tcg_gen_and_i32(dest, src, reg); - DEST_EA(env, insn, OS_LONG, dest, &addr); + DEST_EA(env, insn, opsize, dest, &addr); } else { - SRC_EA(env, src, OS_LONG, 0, NULL); + SRC_EA(env, src, opsize, 0, NULL); tcg_gen_and_i32(dest, src, reg); - tcg_gen_mov_i32(reg, dest); + gen_partset_reg(opsize, reg, dest); } - gen_logic_cc(s, dest); + tcg_temp_free(dest); + gen_logic_cc(s, dest, opsize); } DISAS_INSN(adda) @@ -1868,21 +2246,79 @@ DISAS_INSN(adda) TCGv src; TCGv reg; - SRC_EA(env, src, OS_LONG, 0, NULL); + SRC_EA(env, src, (insn & 0x100) ? OS_LONG : OS_WORD, 1, NULL); reg = AREG(insn, 9); tcg_gen_add_i32(reg, reg, src); } -DISAS_INSN(addx) +static inline void gen_addx(DisasContext *s, TCGv src, TCGv dest, int opsize) { - TCGv reg; + TCGv tmp; + + gen_flush_flags(s); /* compute old Z */ + + /* Perform addition with carry. + * (X, N) = src + dest + X; + */ + + tmp = tcg_const_i32(0); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_X, tmp, dest, tmp); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_N, QREG_CC_X, src, tmp); + gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); + + /* Compute signed-overflow for addition. */ + + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, src); + tcg_gen_xor_i32(tmp, dest, src); + tcg_gen_andc_i32(QREG_CC_V, QREG_CC_V, tmp); + tcg_temp_free(tmp); + + /* Copy the rest of the results into place. */ + tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */ + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + + set_cc_op(s, CC_OP_FLAGS); + + /* result is in QREG_CC_N */ +} + +DISAS_INSN(addx_reg) +{ + TCGv dest; TCGv src; + int opsize; - gen_flush_flags(s); - reg = DREG(insn, 9); - src = DREG(insn, 0); - gen_helper_addx_cc(reg, cpu_env, reg, src); - s->cc_op = CC_OP_FLAGS; + opsize = insn_opsize(insn); + + dest = gen_extend(DREG(insn, 9), opsize, 1); + src = gen_extend(DREG(insn, 0), opsize, 1); + + gen_addx(s, src, dest, opsize); + + gen_partset_reg(opsize, DREG(insn, 9), QREG_CC_N); +} + +DISAS_INSN(addx_mem) +{ + TCGv src; + TCGv addr_src; + TCGv dest; + TCGv addr_dest; + int opsize; + + opsize = insn_opsize(insn); + + addr_src = AREG(insn, 0); + tcg_gen_subi_i32(addr_src, addr_src, opsize_bytes(opsize)); + src = gen_load(s, opsize, addr_src, 1); + + addr_dest = AREG(insn, 9); + tcg_gen_subi_i32(addr_dest, addr_dest, opsize_bytes(opsize)); + dest = gen_load(s, opsize, addr_dest, 1); + + gen_addx(s, src, dest, opsize); + + gen_store(s, opsize, addr_dest, QREG_CC_N); } /* TODO: This could be implemented without helper functions. */ @@ -1892,6 +2328,8 @@ DISAS_INSN(shift_im) int tmp; TCGv shift; + set_cc_op(s, CC_OP_FLAGS); + reg = DREG(insn, 0); tmp = (insn >> 9) & 7; if (tmp == 0) @@ -1907,7 +2345,6 @@ DISAS_INSN(shift_im) gen_helper_sar_cc(reg, cpu_env, reg, shift); } } - s->cc_op = CC_OP_SHIFT; } DISAS_INSN(shift_reg) @@ -1917,8 +2354,6 @@ DISAS_INSN(shift_reg) reg = DREG(insn, 0); shift = DREG(insn, 9); - /* Shift by zero leaves C flag unmodified. */ - gen_flush_flags(s); if (insn & 0x100) { gen_helper_shl_cc(reg, cpu_env, reg, shift); } else { @@ -1928,14 +2363,14 @@ DISAS_INSN(shift_reg) gen_helper_sar_cc(reg, cpu_env, reg, shift); } } - s->cc_op = CC_OP_SHIFT; + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(ff1) { TCGv reg; reg = DREG(insn, 0); - gen_logic_cc(s, reg); + gen_logic_cc(s, reg, OS_LONG); gen_helper_ff1(reg, reg); } @@ -1957,14 +2392,12 @@ DISAS_INSN(strldsr) uint32_t addr; addr = s->pc - 2; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext != 0x46FC) { gen_exception(s, addr, EXCP_UNSUPPORTED); return; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (IS_USER(s) || (ext & SR_S) == 0) { gen_exception(s, addr, EXCP_PRIVILEGE); return; @@ -1975,16 +2408,14 @@ DISAS_INSN(strldsr) DISAS_INSN(move_from_sr) { - TCGv reg; TCGv sr; - if (IS_USER(s)) { + if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) { gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); return; } sr = gen_get_sr(s); - reg = DREG(insn, 0); - gen_partset_reg(OS_WORD, reg, sr); + DEST_EA(env, insn, OS_WORD, sr, NULL); } DISAS_INSN(move_to_sr) @@ -2031,8 +2462,7 @@ DISAS_INSN(stop) return; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); gen_set_sr_im(s, ext, 0); tcg_gen_movi_i32(cpu_halted, 1); @@ -2058,8 +2488,7 @@ DISAS_INSN(movec) return; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext & 0x8000) { reg = AREG(ext, 12); @@ -2125,8 +2554,7 @@ DISAS_INSN(fpu) int set_dest; int opsize; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); opmode = ext & 0x7f; switch ((ext >> 13) & 7) { case 0: case 2: @@ -2408,8 +2836,7 @@ DISAS_INSN(fbcc) offset = cpu_ldsw_code(env, s->pc); s->pc += 2; if (insn & (1 << 6)) { - offset = (offset << 16) | cpu_lduw_code(env, s->pc); - s->pc += 2; + offset = (offset << 16) | read_im16(env, s); } l1 = gen_new_label(); @@ -2534,8 +2961,7 @@ DISAS_INSN(mac) s->done_mac = 1; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); acc = ((insn >> 7) & 1) | ((ext >> 3) & 2); dual = ((insn & 0x30) != 0 && (ext & 3) != 0); @@ -2747,9 +3173,11 @@ DISAS_INSN(from_mext) DISAS_INSN(macsr_to_ccr) { - tcg_gen_movi_i32(QREG_CC_X, 0); - tcg_gen_andi_i32(QREG_CC_DEST, QREG_MACSR, 0xf); - s->cc_op = CC_OP_FLAGS; + TCGv tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_MACSR, 0xf); + gen_helper_set_sr(cpu_env, tmp); + tcg_temp_free(tmp); + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(to_mac) @@ -2841,90 +3269,131 @@ register_opcode (disas_proc proc, uint16_t opcode, uint16_t mask) Later insn override earlier ones. */ void register_m68k_insns (CPUM68KState *env) { + /* Build the opcode table only once to avoid + multithreading issues. */ + if (opcode_table[0] != NULL) { + return; + } + + /* use BASE() for instruction available + * for CF_ISA_A and M68000. + */ +#define BASE(name, opcode, mask) \ + register_opcode(disas_##name, 0x##opcode, 0x##mask) #define INSN(name, opcode, mask, feature) do { \ if (m68k_feature(env, M68K_FEATURE_##feature)) \ - register_opcode(disas_##name, 0x##opcode, 0x##mask); \ + BASE(name, opcode, mask); \ } while(0) - INSN(undef, 0000, 0000, CF_ISA_A); + BASE(undef, 0000, 0000); INSN(arith_im, 0080, fff8, CF_ISA_A); + INSN(arith_im, 0000, ff00, M68000); + INSN(undef, 00c0, ffc0, M68000); INSN(bitrev, 00c0, fff8, CF_ISA_APLUSC); - INSN(bitop_reg, 0100, f1c0, CF_ISA_A); - INSN(bitop_reg, 0140, f1c0, CF_ISA_A); - INSN(bitop_reg, 0180, f1c0, CF_ISA_A); - INSN(bitop_reg, 01c0, f1c0, CF_ISA_A); + BASE(bitop_reg, 0100, f1c0); + BASE(bitop_reg, 0140, f1c0); + BASE(bitop_reg, 0180, f1c0); + BASE(bitop_reg, 01c0, f1c0); INSN(arith_im, 0280, fff8, CF_ISA_A); + INSN(arith_im, 0200, ff00, M68000); + INSN(undef, 02c0, ffc0, M68000); INSN(byterev, 02c0, fff8, CF_ISA_APLUSC); INSN(arith_im, 0480, fff8, CF_ISA_A); + INSN(arith_im, 0400, ff00, M68000); + INSN(undef, 04c0, ffc0, M68000); + INSN(arith_im, 0600, ff00, M68000); + INSN(undef, 06c0, ffc0, M68000); INSN(ff1, 04c0, fff8, CF_ISA_APLUSC); INSN(arith_im, 0680, fff8, CF_ISA_A); - INSN(bitop_im, 0800, ffc0, CF_ISA_A); - INSN(bitop_im, 0840, ffc0, CF_ISA_A); - INSN(bitop_im, 0880, ffc0, CF_ISA_A); - INSN(bitop_im, 08c0, ffc0, CF_ISA_A); - INSN(arith_im, 0a80, fff8, CF_ISA_A); INSN(arith_im, 0c00, ff38, CF_ISA_A); - INSN(move, 1000, f000, CF_ISA_A); - INSN(move, 2000, f000, CF_ISA_A); - INSN(move, 3000, f000, CF_ISA_A); + INSN(arith_im, 0c00, ff00, M68000); + BASE(bitop_im, 0800, ffc0); + BASE(bitop_im, 0840, ffc0); + BASE(bitop_im, 0880, ffc0); + BASE(bitop_im, 08c0, ffc0); + INSN(arith_im, 0a80, fff8, CF_ISA_A); + INSN(arith_im, 0a00, ff00, M68000); + BASE(move, 1000, f000); + BASE(move, 2000, f000); + BASE(move, 3000, f000); INSN(strldsr, 40e7, ffff, CF_ISA_APLUSC); INSN(negx, 4080, fff8, CF_ISA_A); + INSN(negx, 4000, ff00, M68000); + INSN(undef, 40c0, ffc0, M68000); INSN(move_from_sr, 40c0, fff8, CF_ISA_A); - INSN(lea, 41c0, f1c0, CF_ISA_A); - INSN(clr, 4200, ff00, CF_ISA_A); - INSN(undef, 42c0, ffc0, CF_ISA_A); + INSN(move_from_sr, 40c0, ffc0, M68000); + BASE(lea, 41c0, f1c0); + BASE(clr, 4200, ff00); + BASE(undef, 42c0, ffc0); INSN(move_from_ccr, 42c0, fff8, CF_ISA_A); + INSN(move_from_ccr, 42c0, ffc0, M68000); INSN(neg, 4480, fff8, CF_ISA_A); - INSN(move_to_ccr, 44c0, ffc0, CF_ISA_A); + INSN(neg, 4400, ff00, M68000); + INSN(undef, 44c0, ffc0, M68000); + BASE(move_to_ccr, 44c0, ffc0); INSN(not, 4680, fff8, CF_ISA_A); + INSN(not, 4600, ff00, M68000); + INSN(undef, 46c0, ffc0, M68000); INSN(move_to_sr, 46c0, ffc0, CF_ISA_A); - INSN(pea, 4840, ffc0, CF_ISA_A); - INSN(swap, 4840, fff8, CF_ISA_A); - INSN(movem, 48c0, fbc0, CF_ISA_A); - INSN(ext, 4880, fff8, CF_ISA_A); - INSN(ext, 48c0, fff8, CF_ISA_A); - INSN(ext, 49c0, fff8, CF_ISA_A); - INSN(tst, 4a00, ff00, CF_ISA_A); + INSN(linkl, 4808, fff8, M68000); + BASE(pea, 4840, ffc0); + BASE(swap, 4840, fff8); + INSN(bkpt, 4848, fff8, BKPT); + BASE(movem, 48c0, fbc0); + BASE(ext, 4880, fff8); + BASE(ext, 48c0, fff8); + BASE(ext, 49c0, fff8); + BASE(tst, 4a00, ff00); INSN(tas, 4ac0, ffc0, CF_ISA_B); + INSN(tas, 4ac0, ffc0, M68000); INSN(halt, 4ac8, ffff, CF_ISA_A); INSN(pulse, 4acc, ffff, CF_ISA_A); - INSN(illegal, 4afc, ffff, CF_ISA_A); + BASE(illegal, 4afc, ffff); INSN(mull, 4c00, ffc0, CF_ISA_A); + INSN(mull, 4c00, ffc0, LONG_MULDIV); INSN(divl, 4c40, ffc0, CF_ISA_A); + INSN(divl, 4c40, ffc0, LONG_MULDIV); INSN(sats, 4c80, fff8, CF_ISA_B); - INSN(trap, 4e40, fff0, CF_ISA_A); - INSN(link, 4e50, fff8, CF_ISA_A); - INSN(unlk, 4e58, fff8, CF_ISA_A); + BASE(trap, 4e40, fff0); + BASE(link, 4e50, fff8); + BASE(unlk, 4e58, fff8); INSN(move_to_usp, 4e60, fff8, USP); INSN(move_from_usp, 4e68, fff8, USP); - INSN(nop, 4e71, ffff, CF_ISA_A); - INSN(stop, 4e72, ffff, CF_ISA_A); - INSN(rte, 4e73, ffff, CF_ISA_A); - INSN(rts, 4e75, ffff, CF_ISA_A); + BASE(nop, 4e71, ffff); + BASE(stop, 4e72, ffff); + BASE(rte, 4e73, ffff); + BASE(rts, 4e75, ffff); INSN(movec, 4e7b, ffff, CF_ISA_A); - INSN(jump, 4e80, ffc0, CF_ISA_A); - INSN(jump, 4ec0, ffc0, CF_ISA_A); - INSN(addsubq, 5180, f1c0, CF_ISA_A); - INSN(scc, 50c0, f0f8, CF_ISA_A); - INSN(addsubq, 5080, f1c0, CF_ISA_A); + BASE(jump, 4e80, ffc0); + BASE(jump, 4ec0, ffc0); + INSN(addsubq, 5000, f080, M68000); + BASE(addsubq, 5080, f0c0); + INSN(scc, 50c0, f0f8, CF_ISA_A); /* Scc.B Dx */ + INSN(scc, 50c0, f0c0, M68000); /* Scc.B <EA> */ + INSN(dbcc, 50c8, f0f8, M68000); INSN(tpf, 51f8, fff8, CF_ISA_A); /* Branch instructions. */ - INSN(branch, 6000, f000, CF_ISA_A); + BASE(branch, 6000, f000); /* Disable long branch instructions, then add back the ones we want. */ - INSN(undef, 60ff, f0ff, CF_ISA_A); /* All long branches. */ + BASE(undef, 60ff, f0ff); /* All long branches. */ INSN(branch, 60ff, f0ff, CF_ISA_B); INSN(undef, 60ff, ffff, CF_ISA_B); /* bra.l */ INSN(branch, 60ff, ffff, BRAL); + INSN(branch, 60ff, f0ff, BCCL); - INSN(moveq, 7000, f100, CF_ISA_A); + BASE(moveq, 7000, f100); INSN(mvzs, 7100, f100, CF_ISA_B); - INSN(or, 8000, f000, CF_ISA_A); - INSN(divw, 80c0, f0c0, CF_ISA_A); - INSN(addsub, 9000, f000, CF_ISA_A); - INSN(subx, 9180, f1f8, CF_ISA_A); + BASE(or, 8000, f000); + BASE(divw, 80c0, f0c0); + BASE(addsub, 9000, f000); + INSN(undef, 90c0, f0c0, CF_ISA_A); + INSN(subx_reg, 9180, f1f8, CF_ISA_A); + INSN(subx_reg, 9100, f138, M68000); + INSN(subx_mem, 9108, f138, M68000); INSN(suba, 91c0, f1c0, CF_ISA_A); + INSN(suba, 90c0, f0c0, M68000); - INSN(undef_mac, a000, f000, CF_ISA_A); + BASE(undef_mac, a000, f000); INSN(mac, a000, f100, CF_EMAC); INSN(from_mac, a180, f9b0, CF_EMAC); INSN(move_mac, a110, f9fc, CF_EMAC); @@ -2943,12 +3412,22 @@ void register_m68k_insns (CPUM68KState *env) INSN(cmpa, b0c0, f1c0, CF_ISA_B); /* cmpa.w */ INSN(cmp, b080, f1c0, CF_ISA_A); INSN(cmpa, b1c0, f1c0, CF_ISA_A); + INSN(cmp, b000, f100, M68000); + INSN(eor, b100, f100, M68000); + INSN(cmpa, b0c0, f0c0, M68000); INSN(eor, b180, f1c0, CF_ISA_A); - INSN(and, c000, f000, CF_ISA_A); - INSN(mulw, c0c0, f0c0, CF_ISA_A); - INSN(addsub, d000, f000, CF_ISA_A); - INSN(addx, d180, f1f8, CF_ISA_A); + BASE(and, c000, f000); + INSN(exg_dd, c140, f1f8, M68000); + INSN(exg_aa, c148, f1f8, M68000); + INSN(exg_da, c188, f1f8, M68000); + BASE(mulw, c0c0, f0c0); + BASE(addsub, d000, f000); + INSN(undef, d0c0, f0c0, CF_ISA_A); + INSN(addx_reg, d180, f1f8, CF_ISA_A); + INSN(addx_reg, d100, f138, M68000); + INSN(addx_mem, d108, f138, M68000); INSN(adda, d1c0, f1c0, CF_ISA_A); + INSN(adda, d0c0, f0c0, M68000); INSN(shift_im, e080, f0f0, CF_ISA_A); INSN(shift_reg, e0a0, f0f0, CF_ISA_A); INSN(undef_fpu, f000, f000, CF_ISA_A); @@ -2969,8 +3448,7 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) { uint16_t insn; - insn = cpu_lduw_code(env, s->pc); - s->pc += 2; + insn = read_im16(env, s); opcode_table[insn](env, s, insn); } @@ -2995,6 +3473,7 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->cc_op = CC_OP_DYNAMIC; + dc->cc_op_synced = 1; dc->singlestep_enabled = cs->singlestep_enabled; dc->fpcr = env->fpcr; dc->user = (env->sr & SR_S) == 0; @@ -3012,7 +3491,7 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) do { pc_offset = dc->pc - pc_start; gen_throws_exception = NULL; - tcg_gen_insn_start(dc->pc); + tcg_gen_insn_start(dc->pc, dc->cc_op); num_insns++; if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { @@ -3043,20 +3522,20 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) if (unlikely(cs->singlestep_enabled)) { /* Make sure the pc is updated, and raise a debug exception. */ if (!dc->is_jmp) { - gen_flush_cc_op(dc); + update_cc_op(dc); tcg_gen_movi_i32(QREG_PC, dc->pc); } gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); } else { switch(dc->is_jmp) { case DISAS_NEXT: - gen_flush_cc_op(dc); + update_cc_op(dc); gen_jmp_tb(dc, 0, dc->pc); break; default: case DISAS_JUMP: case DISAS_UPDATE: - gen_flush_cc_op(dc); + update_cc_op(dc); /* indicate that the hash table must be used to find the next TB */ tcg_gen_exit_tb(0); break; @@ -3091,20 +3570,24 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, for (i = 0; i < 8; i++) { u.d = env->fregs[i]; - cpu_fprintf (f, "D%d = %08x A%d = %08x F%d = %08x%08x (%12g)\n", - i, env->dregs[i], i, env->aregs[i], - i, u.l.upper, u.l.lower, *(double *)&u.d); + cpu_fprintf(f, "D%d = %08x A%d = %08x F%d = %08x%08x (%12g)\n", + i, env->dregs[i], i, env->aregs[i], + i, u.l.upper, u.l.lower, *(double *)&u.d); } cpu_fprintf (f, "PC = %08x ", env->pc); - sr = env->sr; - cpu_fprintf (f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-', - (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', - (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); + sr = env->sr | cpu_m68k_get_ccr(env); + cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? 'X' : '-', + (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', + (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result); } void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, target_ulong *data) { + int cc_op = data[1]; env->pc = data[0]; + if (cc_op != CC_OP_DYNAMIC) { + env->cc_op = cc_op; + } } diff --git a/target-mips/machine.c b/target-mips/machine.c index a27f2f156d..d20d948457 100644 --- a/target-mips/machine.c +++ b/target-mips/machine.c @@ -2,7 +2,6 @@ #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" -#include "cpu.h" #include "migration/cpu.h" static int cpu_post_load(void *opaque, int version_id) diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 921c39d33f..808760bf53 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -213,7 +213,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) cs->halted = 1; cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } - new_msr |= (target_ulong)MSR_HVB; + if (env->msr_mask & MSR_HVB) { + /* ISA specifies HV, but can be delivered to guest with HV clear + * (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } ail = 0; /* machine check exceptions don't have ME set */ @@ -385,14 +390,23 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) srr1 = SPR_BOOKE_CSRR1; break; case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { /* indicate that we resumed from power save mode */ msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* ISA specifies HV, but can be delivered to guest with HV clear + * (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; } else { - new_msr &= ~((target_ulong)1 << MSR_ME); + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } } - - new_msr |= (target_ulong)MSR_HVB; ail = 0; break; case POWERPC_EXCP_DSEG: /* Data segment exception */ @@ -609,9 +623,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->spr[srr1] = msr; /* Sanity check */ - if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) { - cpu_abort(cs, "Trying to deliver HV exception %d with " - "no HV support\n", excp); + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } } /* If any alternate SRR register are defined, duplicate saved values */ diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index b0760f041d..8a389e19af 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -2362,6 +2362,58 @@ VSX_MADD(xvnmaddmsp, 4, float32, VsrW(i), NMADD_FLGS, 0, 0, 0) VSX_MADD(xvnmsubasp, 4, float32, VsrW(i), NMSUB_FLGS, 1, 0, 0) VSX_MADD(xvnmsubmsp, 4, float32, VsrW(i), NMSUB_FLGS, 0, 0, 0) +/* VSX_SCALAR_CMP_DP - VSX scalar floating point compare double precision + * op - instruction mnemonic + * cmp - comparison operation + * exp - expected result of comparison + * svxvc - set VXVC bit + */ +#define VSX_SCALAR_CMP_DP(op, cmp, exp, svxvc) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + bool vxsnan_flag = false, vxvc_flag = false, vex_flag = false; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \ + vxsnan_flag = true; \ + if (fpscr_ve == 0 && svxvc) { \ + vxvc_flag = true; \ + } \ + } else if (svxvc) { \ + vxvc_flag = float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_quiet_nan(xb.VsrD(0), &env->fp_status); \ + } \ + if (vxsnan_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (vxvc_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + vex_flag = fpscr_ve && (vxvc_flag || vxsnan_flag); \ + \ + if (!vex_flag) { \ + if (float64_##cmp(xb.VsrD(0), xa.VsrD(0), &env->fp_status) == exp) { \ + xt.VsrD(0) = -1; \ + xt.VsrD(1) = 0; \ + } else { \ + xt.VsrD(0) = 0; \ + xt.VsrD(1) = 0; \ + } \ + } \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_SCALAR_CMP_DP(xscmpeqdp, eq, 1, 0) +VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1) +VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1) +VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0) + #define VSX_SCALAR_CMP(op, ordered) \ void helper_##op(CPUPPCState *env, uint32_t opcode) \ { \ @@ -2445,8 +2497,9 @@ VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) * fld - vsr_t field (VsrD(*) or VsrW(*)) * cmp - comparison operation * svxvc - set VXVC bit + * exp - expected result of comparison */ -#define VSX_CMP(op, nels, tp, fld, cmp, svxvc) \ +#define VSX_CMP(op, nels, tp, fld, cmp, svxvc, exp) \ void helper_##op(CPUPPCState *env, uint32_t opcode) \ { \ ppc_vsr_t xt, xa, xb; \ @@ -2471,7 +2524,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.fld = 0; \ all_true = 0; \ } else { \ - if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == 1) { \ + if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == exp) { \ xt.fld = -1; \ all_false = 0; \ } else { \ @@ -2488,12 +2541,14 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ float_check_status(env); \ } -VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0) -VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1) -VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1) -VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0) -VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1) -VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1) +VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) +VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1) +VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1) +VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0) +VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1) +VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1) +VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1) +VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) /* VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion * op - instruction mnemonic diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 04c64217b7..3916b2eddc 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -272,6 +272,8 @@ DEF_HELPER_2(vextsh2w, void, avr, avr) DEF_HELPER_2(vextsb2d, void, avr, avr) DEF_HELPER_2(vextsh2d, void, avr, avr) DEF_HELPER_2(vextsw2d, void, avr, avr) +DEF_HELPER_2(vnegw, void, avr, avr) +DEF_HELPER_2(vnegd, void, avr, avr) DEF_HELPER_2(vupkhpx, void, avr, avr) DEF_HELPER_2(vupklpx, void, avr, avr) DEF_HELPER_2(vupkhsb, void, avr, avr) @@ -387,6 +389,10 @@ DEF_HELPER_2(xsnmaddadp, void, env, i32) DEF_HELPER_2(xsnmaddmdp, void, env, i32) DEF_HELPER_2(xsnmsubadp, void, env, i32) DEF_HELPER_2(xsnmsubmdp, void, env, i32) +DEF_HELPER_2(xscmpeqdp, void, env, i32) +DEF_HELPER_2(xscmpgtdp, void, env, i32) +DEF_HELPER_2(xscmpgedp, void, env, i32) +DEF_HELPER_2(xscmpnedp, void, env, i32) DEF_HELPER_2(xscmpodp, void, env, i32) DEF_HELPER_2(xscmpudp, void, env, i32) DEF_HELPER_2(xsmaxdp, void, env, i32) @@ -448,6 +454,7 @@ DEF_HELPER_2(xvmindp, void, env, i32) DEF_HELPER_2(xvcmpeqdp, void, env, i32) DEF_HELPER_2(xvcmpgedp, void, env, i32) DEF_HELPER_2(xvcmpgtdp, void, env, i32) +DEF_HELPER_2(xvcmpnedp, void, env, i32) DEF_HELPER_2(xvcvdpsp, void, env, i32) DEF_HELPER_2(xvcvdpsxds, void, env, i32) DEF_HELPER_2(xvcvdpsxws, void, env, i32) @@ -485,6 +492,7 @@ DEF_HELPER_2(xvminsp, void, env, i32) DEF_HELPER_2(xvcmpeqsp, void, env, i32) DEF_HELPER_2(xvcmpgesp, void, env, i32) DEF_HELPER_2(xvcmpgtsp, void, env, i32) +DEF_HELPER_2(xvcmpnesp, void, env, i32) DEF_HELPER_2(xvcvspdp, void, env, i32) DEF_HELPER_2(xvcvspsxds, void, env, i32) DEF_HELPER_2(xvcvspsxws, void, env, i32) diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index 5aee0a81c7..dca479838d 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -1949,6 +1949,18 @@ VEXT_SIGNED(vextsh2d, s64, UINT16_MAX, int16_t, int64_t) VEXT_SIGNED(vextsw2d, s64, UINT32_MAX, int32_t, int64_t) #undef VEXT_SIGNED +#define VNEG(name, element) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ +{ \ + int i; \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = -b->element[i]; \ + } \ +} +VNEG(vnegw, s32) +VNEG(vnegd, s64) +#undef VNEG + #define VSPLTI(suffix, element, splat_type) \ void helper_vspltis##suffix(ppc_avr_t *r, uint32_t splat) \ { \ diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 4820f22377..e43cb6c39d 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -8,7 +8,6 @@ #include "helper_regs.h" #include "mmu-hash64.h" #include "migration/cpu.h" -#include "exec/exec-all.h" static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) { diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index 6548715831..1ab8a6eab4 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -23,7 +23,6 @@ #include "exec/helper-proto.h" #include "helper_regs.h" -#include "exec/exec-all.h" #include "exec/cpu_ldst.h" //#define DEBUG_OP diff --git a/target-ppc/translate.c b/target-ppc/translate.c index dab8f19a91..43505a936c 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -322,7 +322,7 @@ static void gen_debug_exception(DisasContext *ctx) */ if ((ctx->exception != POWERPC_EXCP_BRANCH) && (ctx->exception != POWERPC_EXCP_SYNC)) { - gen_update_nip(ctx, ctx->nip - 4); + gen_update_nip(ctx, ctx->nip); } t0 = tcg_const_i32(EXCP_DEBUG); gen_helper_raise_exception(cpu_env, t0); @@ -376,6 +376,9 @@ GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, type2) #define GEN_HANDLER_E_2(name, opc1, opc2, opc3, opc4, inval, type, type2) \ GEN_OPCODE3(name, opc1, opc2, opc3, opc4, inval, type, type2) +#define GEN_HANDLER2_E_2(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) \ +GEN_OPCODE4(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) + typedef struct opcode_t { unsigned char opc1, opc2, opc3, opc4; #if HOST_LONG_BITS == 64 /* Explicitly align to 64 bits */ @@ -662,6 +665,21 @@ EXTRACT_HELPER(IMM8, 11, 8); }, \ .oname = stringify(name), \ } +#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = onam, \ + }, \ + .oname = onam, \ +} #else #define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \ { \ @@ -720,6 +738,20 @@ EXTRACT_HELPER(IMM8, 11, 8); }, \ .oname = stringify(name), \ } +#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = onam, \ +} #endif /* SPR load/store helpers */ diff --git a/target-ppc/translate/vmx-impl.inc.c b/target-ppc/translate/vmx-impl.inc.c index c8998f3eab..fc612d9f37 100644 --- a/target-ppc/translate/vmx-impl.inc.c +++ b/target-ppc/translate/vmx-impl.inc.c @@ -182,6 +182,52 @@ static void gen_mtvscr(DisasContext *ctx) tcg_temp_free_ptr(p); } +#define GEN_VX_VMUL10(name, add_cin, ret_carry) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i64 t1 = tcg_temp_new_i64(); \ + TCGv_i64 t2 = tcg_temp_new_i64(); \ + TCGv_i64 ten, z; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + ten = tcg_const_i64(10); \ + z = tcg_const_i64(0); \ + \ + if (add_cin) { \ + tcg_gen_mulu2_i64(t0, t1, cpu_avrl[rA(ctx->opcode)], ten); \ + tcg_gen_andi_i64(t2, cpu_avrl[rB(ctx->opcode)], 0xF); \ + tcg_gen_add2_i64(cpu_avrl[rD(ctx->opcode)], t2, t0, t1, t2, z); \ + } else { \ + tcg_gen_mulu2_i64(cpu_avrl[rD(ctx->opcode)], t2, \ + cpu_avrl[rA(ctx->opcode)], ten); \ + } \ + \ + if (ret_carry) { \ + tcg_gen_mulu2_i64(t0, t1, cpu_avrh[rA(ctx->opcode)], ten); \ + tcg_gen_add2_i64(t0, cpu_avrl[rD(ctx->opcode)], t0, t1, t2, z); \ + tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); \ + } else { \ + tcg_gen_mul_i64(t0, cpu_avrh[rA(ctx->opcode)], ten); \ + tcg_gen_add_i64(cpu_avrh[rD(ctx->opcode)], t0, t2); \ + } \ + \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ + tcg_temp_free_i64(t2); \ + tcg_temp_free_i64(ten); \ + tcg_temp_free_i64(z); \ +} \ + +GEN_VX_VMUL10(vmul10uq, 0, 0); +GEN_VX_VMUL10(vmul10euq, 1, 0); +GEN_VX_VMUL10(vmul10cuq, 0, 1); +GEN_VX_VMUL10(vmul10ecuq, 1, 1); + /* Logical operations */ #define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -276,8 +322,30 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ } \ } +/* Adds support to provide invalid mask */ +#define GEN_VXFORM_DUAL_EXT(name0, flg0, flg2_0, inval0, \ + name1, flg1, flg2_1, inval1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0)) && \ + !(ctx->opcode & inval0)) { \ + gen_##name0(ctx); \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1)) && \ + !(ctx->opcode & inval1)) { \ + gen_##name1(ctx); \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + GEN_VXFORM(vaddubm, 0, 0); +GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \ + vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800) GEN_VXFORM(vadduhm, 0, 1); +GEN_VXFORM_DUAL(vadduhm, PPC_ALTIVEC, PPC_NONE, \ + vmul10ecuq, PPC_NONE, PPC2_ISA300) GEN_VXFORM(vadduwm, 0, 2); GEN_VXFORM(vaddudm, 0, 3); GEN_VXFORM(vsububm, 0, 16); @@ -390,7 +458,11 @@ GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); GEN_VXFORM(vsubcuw, 0, 22); GEN_VXFORM_ENV(vaddubs, 0, 8); +GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ + vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) GEN_VXFORM_ENV(vadduhs, 0, 9); +GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ + vmul10euq, PPC_NONE, PPC2_ISA300) GEN_VXFORM_ENV(vadduws, 0, 10); GEN_VXFORM_ENV(vaddsbs, 0, 12); GEN_VXFORM_ENV(vaddshs, 0, 13); @@ -815,6 +887,8 @@ GEN_VXFORM_NOA(vclzb, 1, 28) GEN_VXFORM_NOA(vclzh, 1, 29) GEN_VXFORM_NOA(vclzw, 1, 30) GEN_VXFORM_NOA(vclzd, 1, 31) +GEN_VXFORM_NOA_2(vnegw, 1, 24, 6) +GEN_VXFORM_NOA_2(vnegd, 1, 24, 7) GEN_VXFORM_NOA_2(vextsb2w, 1, 24, 16) GEN_VXFORM_NOA_2(vextsh2w, 1, 24, 17) GEN_VXFORM_NOA_2(vextsb2d, 1, 24, 24) diff --git a/target-ppc/translate/vmx-ops.inc.c b/target-ppc/translate/vmx-ops.inc.c index 68cba3e474..cc7ed7eeed 100644 --- a/target-ppc/translate/vmx-ops.inc.c +++ b/target-ppc/translate/vmx-ops.inc.c @@ -55,8 +55,8 @@ GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, type0, type1) GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, tp0, tp1), \ GEN_HANDLER_E(name0##_##name1, 0x4, opc2, (opc3 | 0x10), 0x00000000, tp0, tp1), -GEN_VXFORM(vaddubm, 0, 0), -GEN_VXFORM(vadduhm, 0, 1), +GEN_VXFORM_DUAL(vaddubm, vmul10cuq, 0, 0, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vadduhm, vmul10ecuq, 0, 1, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vadduwm, 0, 2), GEN_VXFORM_207(vaddudm, 0, 3), GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE), @@ -123,8 +123,8 @@ GEN_VXFORM(vslo, 6, 16), GEN_VXFORM(vsro, 6, 17), GEN_VXFORM(vaddcuw, 0, 6), GEN_VXFORM(vsubcuw, 0, 22), -GEN_VXFORM(vaddubs, 0, 8), -GEN_VXFORM(vadduhs, 0, 9), +GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vadduws, 0, 10), GEN_VXFORM(vaddsbs, 0, 12), GEN_VXFORM(vaddshs, 0, 13), @@ -215,6 +215,8 @@ GEN_VXFORM_DUAL_INV(vspltish, vinserth, 6, 13, 0x00000000, 0x100000, GEN_VXFORM_DUAL_INV(vspltisw, vinsertw, 6, 14, 0x00000000, 0x100000, PPC_ALTIVEC), GEN_VXFORM_300_EXT(vinsertd, 6, 15, 0x100000), +GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06), +GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07), GEN_VXFORM_300_EO(vextsb2w, 0x01, 0x18, 0x10), GEN_VXFORM_300_EO(vextsh2w, 0x01, 0x18, 0x11), GEN_VXFORM_300_EO(vextsb2d, 0x01, 0x18, 0x18), diff --git a/target-ppc/translate/vsx-impl.inc.c b/target-ppc/translate/vsx-impl.inc.c index 23ec1e115c..5a27be4bd4 100644 --- a/target-ppc/translate/vsx-impl.inc.c +++ b/target-ppc/translate/vsx-impl.inc.c @@ -132,6 +132,22 @@ static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, tcg_temp_free_i64(mask); } +static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, + TCGv_i64 inh, TCGv_i64 inl) +{ + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 lo = tcg_temp_new_i64(); + + tcg_gen_bswap64_i64(hi, inh); + tcg_gen_bswap64_i64(lo, inl); + tcg_gen_shri_i64(outh, hi, 32); + tcg_gen_deposit_i64(outh, outh, hi, 32, 32); + tcg_gen_shri_i64(outl, lo, 32); + tcg_gen_deposit_i64(outl, outl, lo, 32, 32); + + tcg_temp_free_i64(hi); + tcg_temp_free_i64(lo); +} static void gen_lxvh8x(DisasContext *ctx) { TCGv EA; @@ -604,6 +620,10 @@ GEN_VSX_HELPER_2(xsnmaddadp, 0x04, 0x14, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsnmaddmdp, 0x04, 0x15, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsnmsubadp, 0x04, 0x16, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsnmsubmdp, 0x04, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) @@ -665,6 +685,7 @@ GEN_VSX_HELPER_2(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) @@ -702,6 +723,7 @@ GEN_VSX_HELPER_2(xvminsp, 0x00, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX) @@ -717,6 +739,67 @@ GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) +static void gen_xxbrd(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_bswap64_i64(xth, xbh); + tcg_gen_bswap64_i64(xtl, xbl); +} + +static void gen_xxbrh(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_bswap16x8(xth, xtl, xbh, xbl); +} + +static void gen_xxbrq(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 t0 = tcg_temp_new_i64(); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_bswap64_i64(t0, xbl); + tcg_gen_bswap64_i64(xtl, xbh); + tcg_gen_mov_i64(xth, t0); + tcg_temp_free_i64(t0); +} + +static void gen_xxbrw(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_bswap32x4(xth, xtl, xbh, xbl); +} + #define VSX_LOGICAL(name, tcg_op) \ static void glue(gen_, name)(DisasContext * ctx) \ { \ diff --git a/target-ppc/translate/vsx-ops.inc.c b/target-ppc/translate/vsx-ops.inc.c index 10eb4b9470..3d9104155a 100644 --- a/target-ppc/translate/vsx-ops.inc.c +++ b/target-ppc/translate/vsx-ops.inc.c @@ -39,6 +39,10 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) +#define GEN_XX2FORM_EO(name, opc2, opc3, opc4, fl2) \ +GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 0, opc3, opc4, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 1, opc3, opc4, 0, PPC_NONE, fl2) + #define GEN_XX3FORM(name, opc2, opc3, fl2) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \ @@ -110,6 +114,10 @@ GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX), GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX), GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX), GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX), +GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300), +GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300), +GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300), +GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300), GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), @@ -171,6 +179,7 @@ GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300), GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), @@ -208,6 +217,7 @@ GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300), GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), @@ -222,6 +232,10 @@ GEN_XX2FORM(xvrspic, 0x16, 0x0A, PPC2_VSX), GEN_XX2FORM(xvrspim, 0x12, 0x0B, PPC2_VSX), GEN_XX2FORM(xvrspip, 0x12, 0x0A, PPC2_VSX), GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX), +GEN_XX2FORM_EO(xxbrh, 0x16, 0x1D, 0x07, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrw, 0x16, 0x1D, 0x0F, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrd, 0x16, 0x1D, 0x17, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), #define VSX_LOGICAL(name, opc2, opc3, fl2) \ GEN_XX3FORM(name, opc2, opc3, fl2) diff --git a/target-sparc/machine.c b/target-sparc/machine.c index 59c92f7582..aea6397861 100644 --- a/target-sparc/machine.c +++ b/target-sparc/machine.c @@ -6,10 +6,7 @@ #include "hw/boards.h" #include "qemu/timer.h" -#include "cpu.h" -#include "exec/exec-all.h" #include "migration/cpu.h" -#include "exec/exec-all.h" #ifdef TARGET_SPARC64 static const VMStateDescription vmstate_cpu_timer = { diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index 4c1e48748b..fb0fa56f1e 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -36,7 +36,6 @@ #include "tcg-op.h" #include "qemu/log.h" #include "sysemu/sysemu.h" -#include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/semihost.h" diff --git a/tests/.gitignore b/tests/.gitignore index 64e050e859..c0d7857538 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -67,7 +67,6 @@ test-qmp-marshal.c test-qobject-output-visitor test-rcu-list test-replication -test-rfifolock test-string-input-visitor test-string-output-visitor test-thread-pool diff --git a/tests/Makefile.include b/tests/Makefile.include index 22656eaf26..de516341fd 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -45,7 +45,6 @@ check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-iov$(EXESUF) gcov-files-test-iov-y = util/iov.c check-unit-y += tests/test-aio$(EXESUF) -check-unit-$(CONFIG_POSIX) += tests/test-rfifolock$(EXESUF) check-unit-y += tests/test-throttle$(EXESUF) gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c @@ -282,6 +281,7 @@ check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF) gcov-files-ppc64-y += hw/usb/hcd-uhci.c check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF) gcov-files-ppc64-y += hw/usb/hcd-xhci.c +check-qtest-ppc64-y += $(check-qtest-virtio-y) check-qtest-sh4-y = tests/endianness-test$(EXESUF) @@ -490,7 +490,6 @@ tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) -tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y) tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) @@ -615,7 +614,7 @@ libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o -libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o +libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o tests/rtc-test$(EXESUF): tests/rtc-test.o diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 9c0adce220..70bcafa9e4 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -78,25 +78,23 @@ static void string_bswap16(uint16_t *s, size_t bytes) /** * Verify that the transfer did not corrupt our state at all. */ -static void verify_state(AHCIQState *ahci) +static void verify_state(AHCIQState *ahci, uint64_t hba_old) { int i, j; uint32_t ahci_fingerprint; uint64_t hba_base; - uint64_t hba_stored; AHCICommandHeader cmd; ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID); g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint); /* If we haven't initialized, this is as much as can be validated. */ - if (!ahci->hba_base) { + if (!ahci->enabled) { return; } hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); - hba_stored = (uint64_t)(uintptr_t)ahci->hba_base; - g_assert_cmphex(hba_base, ==, hba_stored); + g_assert_cmphex(hba_base, ==, hba_old); g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap); g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2); @@ -119,12 +117,15 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) QOSState *tmp = to->parent; QPCIDevice *dev = to->dev; char *uri_local = NULL; + uint64_t hba_old; if (uri == NULL) { uri_local = g_strdup_printf("%s%s", "unix:", mig_socket); uri = uri_local; } + hba_old = (uint64_t)qpci_config_readl(from->dev, PCI_BASE_ADDRESS_5); + /* context will be 'to' after completion. */ migrate(from->parent, to->parent, uri); @@ -141,7 +142,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) from->parent = tmp; from->dev = dev; - verify_state(to); + verify_state(to, hba_old); g_free(uri_local); } diff --git a/tests/crypto-tls-x509-helpers.h b/tests/crypto-tls-x509-helpers.h index a8faa92bc0..921341c649 100644 --- a/tests/crypto-tls-x509-helpers.h +++ b/tests/crypto-tls-x509-helpers.h @@ -21,9 +21,6 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> - #if !(defined WIN32) && \ defined(CONFIG_TASN1) && \ (LIBGNUTLS_VERSION_NUMBER >= 0x020600) diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index 3979b20bb0..8c42ca919f 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -87,7 +87,7 @@ typedef struct e1000e_device { QPCIDevice *pci_dev; - void *mac_regs; + QPCIBar mac_regs; uint64_t tx_ring; uint64_t rx_ring; @@ -119,12 +119,12 @@ static QPCIDevice *e1000e_device_find(QPCIBus *bus) static void e1000e_macreg_write(e1000e_device *d, uint32_t reg, uint32_t val) { - qpci_io_writel(d->pci_dev, d->mac_regs + reg, val); + qpci_io_writel(d->pci_dev, d->mac_regs, reg, val); } static uint32_t e1000e_macreg_read(e1000e_device *d, uint32_t reg) { - return qpci_io_readl(d->pci_dev, d->mac_regs + reg); + return qpci_io_readl(d->pci_dev, d->mac_regs, reg); } static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) @@ -138,7 +138,6 @@ static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) /* Map BAR0 (mac registers) */ d->mac_regs = qpci_iomap(d->pci_dev, 0, NULL); - g_assert_nonnull(d->mac_regs); /* Reset the device */ val = e1000e_macreg_read(d, E1000E_CTRL); diff --git a/tests/ide-test.c b/tests/ide-test.c index a8a4081f78..67c7df0c8d 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -137,7 +137,7 @@ static void ide_test_quit(void) qtest_end(); } -static QPCIDevice *get_pci_device(uint16_t *bmdma_base) +static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar) { QPCIDevice *dev; uint16_t vendor_id, device_id; @@ -156,7 +156,9 @@ static QPCIDevice *get_pci_device(uint16_t *bmdma_base) g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1); /* Map bmdma BAR */ - *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL); + *bmdma_bar = qpci_iomap(dev, 4, NULL); + + *ide_bar = qpci_legacy_iomap(dev, IDE_BASE); qpci_device_enable(dev); @@ -179,17 +181,18 @@ typedef struct PrdtEntry { static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, PrdtEntry *prdt, int prdt_entries, - void(*post_exec)(uint64_t sector, int nb_sectors)) + void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t sector, int nb_sectors)) { QPCIDevice *dev; - uint16_t bmdma_base; + QPCIBar bmdma_bar, ide_bar; uintptr_t guest_prdt; size_t len; bool from_dev; uint8_t status; int flags; - dev = get_pci_device(&bmdma_base); + dev = get_pci_device(&bmdma_bar, &ide_bar); flags = cmd & ~0xff; cmd &= 0xff; @@ -214,59 +217,60 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, } /* Select device 0 */ - outb(IDE_BASE + reg_device, 0 | LBA); + qpci_io_writeb(dev, ide_bar, reg_device, 0 | LBA); /* Stop any running transfer, clear any pending interrupt */ - outb(bmdma_base + bmreg_cmd, 0); - outb(bmdma_base + bmreg_status, BM_STS_INTR); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_status, BM_STS_INTR); /* Setup PRDT */ len = sizeof(*prdt) * prdt_entries; guest_prdt = guest_alloc(guest_malloc, len); memwrite(guest_prdt, prdt, len); - outl(bmdma_base + bmreg_prdt, guest_prdt); + qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); /* ATA DMA command */ if (cmd == CMD_PACKET) { /* Enables ATAPI DMA; otherwise PIO is attempted */ - outb(IDE_BASE + reg_feature, 0x01); + qpci_io_writeb(dev, ide_bar, reg_feature, 0x01); } else { - outb(IDE_BASE + reg_nsectors, nb_sectors); - outb(IDE_BASE + reg_lba_low, sector & 0xff); - outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff); - outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_nsectors, nb_sectors); + qpci_io_writeb(dev, ide_bar, reg_lba_low, sector & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, (sector >> 8) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (sector >> 16) & 0xff); } - outb(IDE_BASE + reg_command, cmd); + qpci_io_writeb(dev, ide_bar, reg_command, cmd); if (post_exec) { - post_exec(sector, nb_sectors); + post_exec(dev, ide_bar, sector, nb_sectors); } /* Start DMA transfer */ - outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, + BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); if (flags & CMDF_ABORT) { - outb(bmdma_base + bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); } /* Wait for the DMA transfer to complete */ do { - status = inb(bmdma_base + bmreg_status); + status = qpci_io_readb(dev, bmdma_bar, bmreg_status); } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR)); /* Check IDE status code */ - assert_bit_set(inb(IDE_BASE + reg_status), DRDY); - assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); /* Reading the status register clears the IRQ */ g_assert(!get_irq(IDE_PRIMARY_IRQ)); /* Stop DMA transfer if still active */ if (status & BM_STS_ACTIVE) { - outb(bmdma_base + bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); } free_pci_device(dev); @@ -276,6 +280,8 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, static void test_bmdma_simple_rw(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; uint8_t *buf; uint8_t *cmpbuf; @@ -289,6 +295,8 @@ static void test_bmdma_simple_rw(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + buf = g_malloc(len); cmpbuf = g_malloc(len); @@ -299,7 +307,7 @@ static void test_bmdma_simple_rw(void) status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Write 0xaa pattern to sector 1 */ memset(buf, 0xaa, len); @@ -308,14 +316,14 @@ static void test_bmdma_simple_rw(void) status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Read and verify 0x55 pattern in sector 0 */ memset(cmpbuf, 0x55, len); status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); memread(guest_buf, buf, len); g_assert(memcmp(buf, cmpbuf, len) == 0); @@ -325,7 +333,7 @@ static void test_bmdma_simple_rw(void) status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); memread(guest_buf, buf, len); g_assert(memcmp(buf, cmpbuf, len) == 0); @@ -337,6 +345,8 @@ static void test_bmdma_simple_rw(void) static void test_bmdma_short_prdt(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; PrdtEntry prdt[] = { @@ -346,21 +356,25 @@ static void test_bmdma_short_prdt(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_one_sector_short_prdt(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; /* Read 2 sectors but only give 1 sector in PRDT */ @@ -371,21 +385,25 @@ static void test_bmdma_one_sector_short_prdt(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 2, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_long_prdt(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; PrdtEntry prdt[] = { @@ -395,23 +413,29 @@ static void test_bmdma_long_prdt(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_no_busmaster(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be * able to access it anyway because the Bus Master bit in the PCI command * register isn't set. This is complete nonsense, but it used to be pretty @@ -424,7 +448,7 @@ static void test_bmdma_no_busmaster(void) /* Not entirely clear what the expected result is, but this is what we get * in practice. At least we want to be aware of any changes. */ g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_setup(void) @@ -454,6 +478,8 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes) static void test_identify(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; uint16_t buf[256]; int i; @@ -464,23 +490,25 @@ static void test_identify(void) "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* IDENTIFY command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_IDENTIFY); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); /* Read in the IDENTIFY buffer and check registers */ - data = inb(IDE_BASE + reg_device); + data = qpci_io_readb(dev, ide_bar, reg_device); g_assert_cmpint(data & DEV, ==, 0); for (i = 0; i < 256; i++) { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, DRDY | DRQ); assert_bit_clear(data, BSY | DF | ERR); - ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data); + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); } - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, DRDY); assert_bit_clear(data, BSY | DF | ERR | DRQ); @@ -505,11 +533,15 @@ static void test_identify(void) */ static void make_dirty(uint8_t device) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; size_t len = 512; uintptr_t guest_buf; void* buf; + dev = get_pci_device(&bmdma_bar, &ide_bar); + guest_buf = guest_alloc(guest_malloc, len); buf = g_malloc(len); g_assert(guest_buf); @@ -527,19 +559,23 @@ static void make_dirty(uint8_t device) status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); g_free(buf); } static void test_flush(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; ide_test_start( "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", tmp_path); + dev = get_pci_device(&bmdma_bar, &ide_bar); + qtest_irq_intercept_in(global_qtest, "ioapic"); /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ @@ -549,11 +585,11 @@ static void test_flush(void) g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\"")); /* FLUSH CACHE command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); /* Check status while request is in flight*/ - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, BSY | DRDY); assert_bit_clear(data, DF | ERR | DRQ); @@ -561,11 +597,11 @@ static void test_flush(void) g_free(hmp("qemu-io ide0-hd0 \"resume A\"")); /* Check registers */ - data = inb(IDE_BASE + reg_device); + data = qpci_io_readb(dev, ide_bar, reg_device); g_assert_cmpint(data & DEV, ==, 0); do { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); } while (data & BSY); assert_bit_set(data, DRDY); @@ -576,6 +612,8 @@ static void test_flush(void) static void test_retry_flush(const char *machine) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; const char *s; @@ -587,17 +625,19 @@ static void test_retry_flush(const char *machine) "rerror=stop,werror=stop", debug_path, tmp_path); + dev = get_pci_device(&bmdma_bar, &ide_bar); + qtest_irq_intercept_in(global_qtest, "ioapic"); /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ make_dirty(0); /* FLUSH CACHE command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); /* Check status while request is in flight*/ - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, BSY | DRDY); assert_bit_clear(data, DF | ERR | DRQ); @@ -608,11 +648,11 @@ static void test_retry_flush(const char *machine) qmp_discard_response(s); /* Check registers */ - data = inb(IDE_BASE + reg_device); + data = qpci_io_readb(dev, ide_bar, reg_device); g_assert_cmpint(data & DEV, ==, 0); do { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); } while (data & BSY); assert_bit_set(data, DRDY); @@ -623,11 +663,16 @@ static void test_retry_flush(const char *machine) static void test_flush_nodev(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + ide_test_start(""); + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* FLUSH CACHE command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); /* Just testing that qemu doesn't crash... */ @@ -654,7 +699,8 @@ typedef struct Read10CDB { uint16_t padding; } __attribute__((__packed__)) Read10CDB; -static void send_scsi_cdb_read10(uint64_t lba, int nblocks) +static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t lba, int nblocks) { Read10CDB pkt = { .padding = 0 }; int i; @@ -670,7 +716,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks) /* Send Packet */ for (i = 0; i < sizeof(Read10CDB)/2; i++) { - outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i])); + qpci_io_writew(dev, ide_bar, reg_data, + le16_to_cpu(((uint16_t *)&pkt)[i])); } } @@ -683,13 +730,17 @@ static void nsleep(int64_t nsecs) static uint8_t ide_wait_clear(uint8_t flag) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; time_t st; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Wait with a 5 second timeout */ time(&st); while (true) { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); if (!(data & flag)) { return data; } @@ -723,6 +774,8 @@ static void ide_wait_intr(int irq) static void cdrom_pio_impl(int nblocks) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; FILE *fh; int patt_blocks = MAX(16, nblocks); size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; @@ -741,13 +794,14 @@ static void cdrom_pio_impl(int nblocks) ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + dev = get_pci_device(&bmdma_bar, &ide_bar); qtest_irq_intercept_in(global_qtest, "ioapic"); /* PACKET command on device 0 */ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); - outb(IDE_BASE + reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); - outb(IDE_BASE + reg_command, CMD_PACKET); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); /* HP0: Check_Status_A State */ nsleep(400); data = ide_wait_clear(BSY); @@ -756,7 +810,7 @@ static void cdrom_pio_impl(int nblocks) assert_bit_clear(data, ERR | DF | BSY); /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(0, nblocks); + send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. @@ -780,7 +834,8 @@ static void cdrom_pio_impl(int nblocks) /* HP4: Transfer_Data */ for (j = 0; j < MIN((limit / 2), rem); j++) { - rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data)); + rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, + reg_data)); } } diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index f36bfe7d0a..04a5c5dc7d 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -41,7 +41,7 @@ static QPCIDevice *get_device(QPCIBus *pcibus) typedef struct _IVState { QTestState *qtest; - void *reg_base, *mem_base; + QPCIBar reg_bar, mem_bar; QPCIBus *pcibus; QPCIDevice *dev; } IVState; @@ -75,7 +75,7 @@ static inline unsigned in_reg(IVState *s, enum Reg reg) unsigned res; global_qtest = s->qtest; - res = qpci_io_readl(s->dev, s->reg_base + reg); + res = qpci_io_readl(s->dev, s->reg_bar, reg); g_test_message("*%s -> %x\n", name, res); global_qtest = qtest; @@ -89,7 +89,26 @@ static inline void out_reg(IVState *s, enum Reg reg, unsigned v) global_qtest = s->qtest; g_test_message("%x -> *%s\n", v, name); - qpci_io_writel(s->dev, s->reg_base + reg, v); + qpci_io_writel(s->dev, s->reg_bar, reg, v); + global_qtest = qtest; +} + +static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) +{ + QTestState *qtest = global_qtest; + + global_qtest = s->qtest; + qpci_memread(s->dev, s->mem_bar, off, buf, len); + global_qtest = qtest; +} + +static inline void write_mem(IVState *s, uint64_t off, + const void *buf, size_t len) +{ + QTestState *qtest = global_qtest; + + global_qtest = s->qtest; + qpci_memwrite(s->dev, s->mem_bar, off, buf, len); global_qtest = qtest; } @@ -108,16 +127,14 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) s->pcibus = qpci_init_pc(NULL); s->dev = get_device(s->pcibus); - s->reg_base = qpci_iomap(s->dev, 0, &barsize); - g_assert_nonnull(s->reg_base); + s->reg_bar = qpci_iomap(s->dev, 0, &barsize); g_assert_cmpuint(barsize, ==, 256); if (msix) { qpci_msix_enable(s->dev); } - s->mem_base = qpci_iomap(s->dev, 2, &barsize); - g_assert_nonnull(s->mem_base); + s->mem_bar = qpci_iomap(s->dev, 2, &barsize); g_assert_cmpuint(barsize, ==, TMPSHMSIZE); qpci_device_enable(s->dev); @@ -169,7 +186,7 @@ static void test_ivshmem_single(void) for (i = 0; i < G_N_ELEMENTS(data); i++) { data[i] = i; } - qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + write_mem(s, 0, data, sizeof(data)); /* verify write */ for (i = 0; i < G_N_ELEMENTS(data); i++) { @@ -178,7 +195,7 @@ static void test_ivshmem_single(void) /* read it back and verify read */ memset(data, 0, sizeof(data)); - qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + read_mem(s, 0, data, sizeof(data)); for (i = 0; i < G_N_ELEMENTS(data); i++) { g_assert_cmpuint(data[i], ==, i); } @@ -201,29 +218,29 @@ static void test_ivshmem_pair(void) /* host write, guest 1 & 2 read */ memset(tmpshmem, 0x42, TMPSHMSIZE); - qtest_memread(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE); + read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x42); } - qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x42); } /* guest 1 write, guest 2 read */ memset(data, 0x43, TMPSHMSIZE); - qtest_memwrite(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE); + write_mem(s1, 0, data, TMPSHMSIZE); memset(data, 0, TMPSHMSIZE); - qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x43); } /* guest 2 write, guest 1 read */ memset(data, 0x44, TMPSHMSIZE); - qtest_memwrite(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + write_mem(s2, 0, data, TMPSHMSIZE); memset(data, 0, TMPSHMSIZE); - qtest_memread(s1->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x44); } diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 716ab7939e..5180d65279 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -210,8 +210,7 @@ void ahci_pci_enable(AHCIQState *ahci) void start_ahci_device(AHCIQState *ahci) { /* Map AHCI's ABAR (BAR5) */ - ahci->hba_base = qpci_iomap(ahci->dev, 5, &ahci->barsize); - g_assert(ahci->hba_base); + ahci->hba_bar = qpci_iomap(ahci->dev, 5, &ahci->barsize); /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ qpci_device_enable(ahci->dev); @@ -351,6 +350,7 @@ void ahci_hba_enable(AHCIQState *ahci) reg = ahci_rreg(ahci, AHCI_GHC); ASSERT_BIT_SET(reg, AHCI_GHC_IE); + ahci->enabled = true; /* TODO: The device should now be idling and waiting for commands. * In the future, a small test-case to inspect the Register D2H FIS * and clear the initial interrupts might be good. */ diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index c69fb5ae90..caaafe3fdf 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -321,12 +321,13 @@ typedef struct AHCIPortQState { typedef struct AHCIQState { QOSState *parent; QPCIDevice *dev; - void *hba_base; + QPCIBar hba_bar; uint64_t barsize; uint32_t fingerprint; uint32_t cap; uint32_t cap2; AHCIPortQState port[32]; + bool enabled; } AHCIQState; /** @@ -488,12 +489,12 @@ typedef struct AHCIOpts { static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset) { - return qpci_io_readl(ahci->dev, ahci->hba_base + offset); + return qpci_io_readl(ahci->dev, ahci->hba_bar, offset); } static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value) { - qpci_io_writel(ahci->dev, ahci->hba_base + offset, value); + qpci_io_writel(ahci->dev, ahci->hba_bar, offset, value); } static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num) diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 7abb48254e..6226546c28 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -10,6 +10,8 @@ /** * Launch QEMU with the given command line, * and then set up interrupts and our guest malloc interface. + * Never returns NULL: + * Terminates the application in case an error is encountered. */ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) { diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 9600ed6e41..ded1c54c06 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -17,7 +17,6 @@ #include "hw/pci/pci_regs.h" #include "qemu-common.h" -#include "qemu/host-utils.h" #define ACPI_PCIHP_ADDR 0xae00 @@ -26,89 +25,58 @@ typedef struct QPCIBusPC { QPCIBus bus; - - uint32_t pci_hole_start; - uint32_t pci_hole_size; - uint32_t pci_hole_alloc; - - uint16_t pci_iohole_start; - uint16_t pci_iohole_size; - uint16_t pci_iohole_alloc; } QPCIBusPC; -static uint8_t qpci_pc_io_readb(QPCIBus *bus, void *addr) +static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; - uint8_t value; - - if (port < 0x10000) { - value = inb(port); - } else { - value = readb(port); - } - - return value; + return inb(addr); } -static uint16_t qpci_pc_io_readw(QPCIBus *bus, void *addr) +static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { - uintptr_t port = (uintptr_t)addr; - uint16_t value; - - if (port < 0x10000) { - value = inw(port); - } else { - value = readw(port); - } - - return value; + outb(addr, val); } -static uint32_t qpci_pc_io_readl(QPCIBus *bus, void *addr) +static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; - uint32_t value; - - if (port < 0x10000) { - value = inl(port); - } else { - value = readl(port); - } + return inw(addr); +} - return value; +static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) +{ + outw(addr, val); } -static void qpci_pc_io_writeb(QPCIBus *bus, void *addr, uint8_t value) +static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; + return inl(addr); +} - if (port < 0x10000) { - outb(port, value); - } else { - writeb(port, value); - } +static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) +{ + outl(addr, val); } -static void qpci_pc_io_writew(QPCIBus *bus, void *addr, uint16_t value) +static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; + return (uint64_t)inl(addr) + ((uint64_t)inl(addr + 4) << 32); +} - if (port < 0x10000) { - outw(port, value); - } else { - writew(port, value); - } +static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) +{ + outl(addr, val & 0xffffffff); + outl(addr + 4, val >> 32); } -static void qpci_pc_io_writel(QPCIBus *bus, void *addr, uint32_t value) +static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { - uintptr_t port = (uintptr_t)addr; + memread(addr, buf, len); +} - if (port < 0x10000) { - outl(port, value); - } else { - writel(port, value); - } +static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + memwrite(addr, buf, len); } static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) @@ -147,84 +115,24 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3 outl(0xcfc, value); } -static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr) -{ - QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); - static const int bar_reg_map[] = { - PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, - }; - int bar_reg; - uint32_t addr; - uint64_t size; - uint32_t io_type; - - g_assert(barno >= 0 && barno <= 5); - bar_reg = bar_reg_map[barno]; - - qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); - addr = qpci_config_readl(dev, bar_reg); - - io_type = addr & PCI_BASE_ADDRESS_SPACE; - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - addr &= PCI_BASE_ADDRESS_IO_MASK; - } else { - addr &= PCI_BASE_ADDRESS_MEM_MASK; - } - - size = (1ULL << ctzl(addr)); - if (size == 0) { - return NULL; - } - if (sizeptr) { - *sizeptr = size; - } - - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - uint16_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size - <= s->pci_iohole_size); - s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); - loc = s->pci_iohole_start + s->pci_iohole_alloc; - s->pci_iohole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); - - return (void *)(intptr_t)loc; - } else { - uint64_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size - <= s->pci_hole_size); - s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); - loc = s->pci_hole_start + s->pci_hole_alloc; - s->pci_hole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc); - - return (void *)(intptr_t)loc; - } -} - -static void qpci_pc_iounmap(QPCIBus *bus, void *data) -{ - /* FIXME */ -} - QPCIBus *qpci_init_pc(QGuestAllocator *alloc) { QPCIBusPC *ret; ret = g_malloc(sizeof(*ret)); - ret->bus.io_readb = qpci_pc_io_readb; - ret->bus.io_readw = qpci_pc_io_readw; - ret->bus.io_readl = qpci_pc_io_readl; + ret->bus.pio_readb = qpci_pc_pio_readb; + ret->bus.pio_readw = qpci_pc_pio_readw; + ret->bus.pio_readl = qpci_pc_pio_readl; + ret->bus.pio_readq = qpci_pc_pio_readq; - ret->bus.io_writeb = qpci_pc_io_writeb; - ret->bus.io_writew = qpci_pc_io_writew; - ret->bus.io_writel = qpci_pc_io_writel; + ret->bus.pio_writeb = qpci_pc_pio_writeb; + ret->bus.pio_writew = qpci_pc_pio_writew; + ret->bus.pio_writel = qpci_pc_pio_writel; + ret->bus.pio_writeq = qpci_pc_pio_writeq; + + ret->bus.memread = qpci_pc_memread; + ret->bus.memwrite = qpci_pc_memwrite; ret->bus.config_readb = qpci_pc_config_readb; ret->bus.config_readw = qpci_pc_config_readw; @@ -234,16 +142,9 @@ QPCIBus *qpci_init_pc(QGuestAllocator *alloc) ret->bus.config_writew = qpci_pc_config_writew; ret->bus.config_writel = qpci_pc_config_writel; - ret->bus.iomap = qpci_pc_iomap; - ret->bus.iounmap = qpci_pc_iounmap; - - ret->pci_hole_start = 0xE0000000; - ret->pci_hole_size = 0x20000000; - ret->pci_hole_alloc = 0; - - ret->pci_iohole_start = 0xc000; - ret->pci_iohole_size = 0x4000; - ret->pci_iohole_alloc = 0; + ret->bus.pio_alloc_ptr = 0xc000; + ret->bus.mmio_alloc_ptr = 0xE0000000; + ret->bus.mmio_limit = 0x100000000ULL; return &ret->bus; } diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index 2eaaf9159a..1e5d015bd4 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -34,14 +34,6 @@ typedef struct QPCIBusSPAPR { uint64_t mmio32_cpu_base; QPCIWindow mmio32; - - uint64_t pci_hole_start; - uint64_t pci_hole_size; - uint64_t pci_hole_alloc; - - uint32_t pci_iohole_start; - uint32_t pci_iohole_size; - uint32_t pci_iohole_alloc; } QPCIBusSPAPR; /* @@ -50,78 +42,66 @@ typedef struct QPCIBusSPAPR { * so PCI accessors need to swap data endianness */ -static uint8_t qpci_spapr_io_readb(QPCIBus *bus, void *addr) +static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return readb(s->pio_cpu_base + addr); +} + +static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + writeb(s->pio_cpu_base + addr, val); +} + +static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return bswap16(readw(s->pio_cpu_base + addr)); +} + +static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - uint8_t v; - if (port < s->pio.size) { - v = readb(s->pio_cpu_base + port); - } else { - v = readb(s->mmio32_cpu_base + port); - } - return v; + writew(s->pio_cpu_base + addr, bswap16(val)); } -static uint16_t qpci_spapr_io_readw(QPCIBus *bus, void *addr) +static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - uint16_t v; - if (port < s->pio.size) { - v = readw(s->pio_cpu_base + port); - } else { - v = readw(s->mmio32_cpu_base + port); - } - return bswap16(v); + return bswap32(readl(s->pio_cpu_base + addr)); } -static uint32_t qpci_spapr_io_readl(QPCIBus *bus, void *addr) +static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - uint32_t v; - if (port < s->pio.size) { - v = readl(s->pio_cpu_base + port); - } else { - v = readl(s->mmio32_cpu_base + port); - } - return bswap32(v); + writel(s->pio_cpu_base + addr, bswap32(val)); } -static void qpci_spapr_io_writeb(QPCIBus *bus, void *addr, uint8_t value) +static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - if (port < s->pio.size) { - writeb(s->pio_cpu_base + port, value); - } else { - writeb(s->mmio32_cpu_base + port, value); - } + return bswap64(readq(s->pio_cpu_base + addr)); } -static void qpci_spapr_io_writew(QPCIBus *bus, void *addr, uint16_t value) +static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - value = bswap16(value); - if (port < s->pio.size) { - writew(s->pio_cpu_base + port, value); - } else { - writew(s->mmio32_cpu_base + port, value); - } + writeq(s->pio_cpu_base + addr, bswap64(val)); } -static void qpci_spapr_io_writel(QPCIBus *bus, void *addr, uint32_t value) +static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr, + void *buf, size_t len) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - value = bswap32(value); - if (port < s->pio.size) { - writel(s->pio_cpu_base + port, value); - } else { - writel(s->mmio32_cpu_base + port, value); - } + memread(s->mmio32_cpu_base + addr, buf, len); +} + +static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + memwrite(s->mmio32_cpu_base + addr, buf, len); } static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset) @@ -169,72 +149,6 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 4, value); } -static void *qpci_spapr_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, - uint64_t *sizeptr) -{ - QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - static const int bar_reg_map[] = { - PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, - }; - int bar_reg; - uint32_t addr; - uint64_t size; - uint32_t io_type; - - g_assert(barno >= 0 && barno <= 5); - bar_reg = bar_reg_map[barno]; - - qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); - addr = qpci_config_readl(dev, bar_reg); - - io_type = addr & PCI_BASE_ADDRESS_SPACE; - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - addr &= PCI_BASE_ADDRESS_IO_MASK; - } else { - addr &= PCI_BASE_ADDRESS_MEM_MASK; - } - - size = (1ULL << ctzl(addr)); - if (size == 0) { - return NULL; - } - if (sizeptr) { - *sizeptr = size; - } - - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - uint16_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size - <= s->pci_iohole_size); - s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); - loc = s->pci_iohole_start + s->pci_iohole_alloc; - s->pci_iohole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); - - return (void *)(unsigned long)loc; - } else { - uint64_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size - <= s->pci_hole_size); - s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); - loc = s->pci_hole_start + s->pci_hole_alloc; - s->pci_hole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc); - - return (void *)(unsigned long)loc; - } -} - -static void qpci_spapr_iounmap(QPCIBus *bus, void *data) -{ - /* FIXME */ -} - #define SPAPR_PCI_BASE (1ULL << 45) #define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ @@ -248,13 +162,18 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->alloc = alloc; - ret->bus.io_readb = qpci_spapr_io_readb; - ret->bus.io_readw = qpci_spapr_io_readw; - ret->bus.io_readl = qpci_spapr_io_readl; + ret->bus.pio_readb = qpci_spapr_pio_readb; + ret->bus.pio_readw = qpci_spapr_pio_readw; + ret->bus.pio_readl = qpci_spapr_pio_readl; + ret->bus.pio_readq = qpci_spapr_pio_readq; - ret->bus.io_writeb = qpci_spapr_io_writeb; - ret->bus.io_writew = qpci_spapr_io_writew; - ret->bus.io_writel = qpci_spapr_io_writel; + ret->bus.pio_writeb = qpci_spapr_pio_writeb; + ret->bus.pio_writew = qpci_spapr_pio_writew; + ret->bus.pio_writel = qpci_spapr_pio_writel; + ret->bus.pio_writeq = qpci_spapr_pio_writeq; + + ret->bus.memread = qpci_spapr_memread; + ret->bus.memwrite = qpci_spapr_memwrite; ret->bus.config_readb = qpci_spapr_config_readb; ret->bus.config_readw = qpci_spapr_config_readw; @@ -264,9 +183,6 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->bus.config_writew = qpci_spapr_config_writew; ret->bus.config_writel = qpci_spapr_config_writel; - ret->bus.iomap = qpci_spapr_iomap; - ret->bus.iounmap = qpci_spapr_iounmap; - /* FIXME: We assume the default location of the PHB for now. * Ideally we'd parse the device tree deposited in the guest to * get the window locations */ @@ -281,15 +197,9 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->mmio32.pci_base = 0x80000000; /* 2 GiB */ ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; - ret->pci_hole_start = 0xC0000000; - ret->pci_hole_size = - ret->mmio32.pci_base + ret->mmio32.size - ret->pci_hole_start; - ret->pci_hole_alloc = 0; - - ret->pci_iohole_start = 0xc000; - ret->pci_iohole_size = - ret->pio.pci_base + ret->pio.size - ret->pci_iohole_start; - ret->pci_iohole_alloc = 0; + ret->bus.pio_alloc_ptr = 0xc000; + ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base; + ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size; return &ret->bus; } diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index c3f3382b7c..2dcdeade2a 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -14,6 +14,7 @@ #include "libqos/pci.h" #include "hw/pci/pci_regs.h" +#include "qemu/host-utils.h" void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), @@ -103,7 +104,6 @@ void qpci_msix_enable(QPCIDevice *dev) 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); @@ -113,18 +113,16 @@ void qpci_msix_enable(QPCIDevice *dev) 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); + dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL); + dev->msix_table_off = 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_bar = qpci_iomap(dev, bir_pba, NULL); } - dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK; - g_assert(dev->msix_table != NULL); - g_assert(dev->msix_pba != NULL); dev->msix_enabled = true; } @@ -140,22 +138,23 @@ void qpci_msix_disable(QPCIDevice *dev) 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); + qpci_iounmap(dev, dev->msix_table_bar); + qpci_iounmap(dev, dev->msix_pba_bar); dev->msix_enabled = 0; - dev->msix_table = NULL; - dev->msix_pba = NULL; + dev->msix_table_off = 0; + dev->msix_pba_off = 0; } 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; + uint64_t off = (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)); + pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); + qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, + pba_entry & ~(1 << bit_n)); return (pba_entry & (1 << bit_n)) != 0; } @@ -163,7 +162,7 @@ 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); + uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE; g_assert(dev->msix_enabled); addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); @@ -173,8 +172,9 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) 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; + return (qpci_io_readl(dev, dev->msix_table_bar, + vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL) + & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; } } @@ -221,46 +221,174 @@ void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) dev->bus->config_writel(dev->bus, dev->devfn, offset, value); } +uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readb(dev->bus, token.addr + off); + } else { + uint8_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return val; + } +} + +uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readw(dev->bus, token.addr + off); + } else { + uint16_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le16_to_cpu(val); + } +} + +uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readl(dev->bus, token.addr + off); + } else { + uint32_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le32_to_cpu(val); + } +} -uint8_t qpci_io_readb(QPCIDevice *dev, void *data) +uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off) { - return dev->bus->io_readb(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readq(dev->bus, token.addr + off); + } else { + uint64_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le64_to_cpu(val); + } } -uint16_t qpci_io_readw(QPCIDevice *dev, void *data) +void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint8_t value) { - return dev->bus->io_readw(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writeb(dev->bus, token.addr + off, value); + } else { + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } +} + +void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint16_t value) +{ + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writew(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le16(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } -uint32_t qpci_io_readl(QPCIDevice *dev, void *data) +void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint32_t value) { - return dev->bus->io_readl(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writel(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le32(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } +void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint64_t value) +{ + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writeq(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le64(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } +} -void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value) +void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off, + void *buf, size_t len) { - dev->bus->io_writeb(dev->bus, data, value); + g_assert(token.addr >= QPCI_PIO_LIMIT); + dev->bus->memread(dev->bus, token.addr + off, buf, len); } -void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value) +void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off, + const void *buf, size_t len) { - dev->bus->io_writew(dev->bus, data, value); + g_assert(token.addr >= QPCI_PIO_LIMIT); + dev->bus->memwrite(dev->bus, token.addr + off, buf, len); } -void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value) +QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) { - dev->bus->io_writel(dev->bus, data, value); + QPCIBus *bus = dev->bus; + static const int bar_reg_map[] = { + PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, + }; + QPCIBar bar; + int bar_reg; + uint32_t addr, size; + uint32_t io_type; + uint64_t loc; + + g_assert(barno >= 0 && barno <= 5); + bar_reg = bar_reg_map[barno]; + + qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); + addr = qpci_config_readl(dev, bar_reg); + + io_type = addr & PCI_BASE_ADDRESS_SPACE; + if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { + addr &= PCI_BASE_ADDRESS_IO_MASK; + } else { + addr &= PCI_BASE_ADDRESS_MEM_MASK; + } + + g_assert(addr); /* Must have *some* size bits */ + + size = 1U << ctz32(addr); + if (sizeptr) { + *sizeptr = size; + } + + if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { + loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size); + + g_assert(loc >= bus->pio_alloc_ptr); + g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */ + + bus->pio_alloc_ptr = loc + size; + + qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); + } else { + loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size); + + /* Check for space */ + g_assert(loc >= bus->mmio_alloc_ptr); + g_assert(loc + size <= bus->mmio_limit); + + bus->mmio_alloc_ptr = loc + size; + + qpci_config_writel(dev, bar_reg, loc); + } + + bar.addr = loc; + return bar; } -void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) +void qpci_iounmap(QPCIDevice *dev, QPCIBar bar) { - return dev->bus->iomap(dev->bus, dev, barno, sizeptr); + /* FIXME */ } -void qpci_iounmap(QPCIDevice *dev, void *data) +QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) { - dev->bus->iounmap(dev->bus, data); + QPCIBar bar = { .addr = addr }; + return bar; } void qpci_plug_device_test(const char *driver, const char *id, diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index c06add8dbf..ed480614ff 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -15,20 +15,27 @@ #include "libqtest.h" +#define QPCI_PIO_LIMIT 0x10000 + #define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn)) typedef struct QPCIDevice QPCIDevice; typedef struct QPCIBus QPCIBus; +typedef struct QPCIBar QPCIBar; -struct QPCIBus -{ - uint8_t (*io_readb)(QPCIBus *bus, void *addr); - uint16_t (*io_readw)(QPCIBus *bus, void *addr); - uint32_t (*io_readl)(QPCIBus *bus, void *addr); +struct QPCIBus { + uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr); + uint16_t (*pio_readw)(QPCIBus *bus, uint32_t addr); + uint32_t (*pio_readl)(QPCIBus *bus, uint32_t addr); + uint64_t (*pio_readq)(QPCIBus *bus, uint32_t addr); - void (*io_writeb)(QPCIBus *bus, void *addr, uint8_t value); - void (*io_writew)(QPCIBus *bus, void *addr, uint16_t value); - void (*io_writel)(QPCIBus *bus, void *addr, uint32_t value); + void (*pio_writeb)(QPCIBus *bus, uint32_t addr, uint8_t value); + void (*pio_writew)(QPCIBus *bus, uint32_t addr, uint16_t value); + void (*pio_writel)(QPCIBus *bus, uint32_t addr, uint32_t value); + void (*pio_writeq)(QPCIBus *bus, uint32_t addr, uint64_t value); + + void (*memread)(QPCIBus *bus, uint32_t addr, void *buf, size_t len); + void (*memwrite)(QPCIBus *bus, uint32_t addr, const void *buf, size_t len); uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset); uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset); @@ -41,8 +48,12 @@ struct QPCIBus void (*config_writel)(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value); - void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr); - void (*iounmap)(QPCIBus *bus, void *data); + uint16_t pio_alloc_ptr; + uint64_t mmio_alloc_ptr, mmio_limit; +}; + +struct QPCIBar { + uint64_t addr; }; struct QPCIDevice @@ -50,8 +61,8 @@ struct QPCIDevice QPCIBus *bus; int devfn; bool msix_enabled; - void *msix_table; - void *msix_pba; + QPCIBar msix_table_bar, msix_pba_bar; + uint64_t msix_table_off, msix_pba_off; }; void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, @@ -75,16 +86,27 @@ void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value); void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value); void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value); -uint8_t qpci_io_readb(QPCIDevice *dev, void *data); -uint16_t qpci_io_readw(QPCIDevice *dev, void *data); -uint32_t qpci_io_readl(QPCIDevice *dev, void *data); - -void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value); -void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value); -void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value); - -void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); -void qpci_iounmap(QPCIDevice *dev, void *data); +uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off); + +void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint8_t value); +void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint16_t value); +void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint32_t value); +void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint64_t value); + +void qpci_memread(QPCIDevice *bus, QPCIBar token, uint64_t off, + void *buf, size_t len); +void qpci_memwrite(QPCIDevice *bus, QPCIBar token, uint64_t off, + const void *buf, size_t len); +QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); +void qpci_iounmap(QPCIDevice *dev, QPCIBar addr); +QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr); void qpci_plug_device_test(const char *driver, const char *id, uint8_t slot, const char *opts); diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c index f794d92da5..72d7a961fe 100644 --- a/tests/libqos/usb.c +++ b/tests/libqos/usb.c @@ -21,14 +21,12 @@ void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar) hc->dev = qpci_device_find(pcibus, devfn); g_assert(hc->dev != NULL); qpci_device_enable(hc->dev); - hc->base = qpci_iomap(hc->dev, bar, NULL); - g_assert(hc->base != NULL); + hc->bar = qpci_iomap(hc->dev, bar, NULL); } void uhci_port_test(struct qhc *hc, int port, uint16_t expect) { - void *addr = hc->base + 0x10 + 2 * port; - uint16_t value = qpci_io_readw(hc->dev, addr); + uint16_t value = qpci_io_readw(hc->dev, hc->bar, 0x10 + 2 * port); uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1); g_assert((value & mask) == (expect & mask)); diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h index 8fe56872b7..423dcfd82f 100644 --- a/tests/libqos/usb.h +++ b/tests/libqos/usb.h @@ -5,7 +5,7 @@ struct qhc { QPCIDevice *dev; - void *base; + QPCIBar bar; }; void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index 0cab38f296..7aa8383338 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -15,28 +15,28 @@ #include "libqos/malloc-generic.h" #include "standard-headers/linux/virtio_ring.h" -static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) +static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readb(dev->addr + addr); + return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) +static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readw(dev->addr + addr); + return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) +static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readl(dev->addr + addr); + return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) +static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readq(dev->addr + addr); + return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) @@ -199,6 +199,7 @@ QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) dev->addr = addr; dev->page_size = page_size; dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + dev->vdev.bus = &qvirtio_mmio; writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 6e005c1835..d4bf841f23 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -62,73 +62,87 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) *vpcidev = (QVirtioPCIDevice *)d; } -static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) +#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) + +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); + return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); } -static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) +/* PCI is always read in little-endian order + * but virtio ( < 1.0) is in guest order + * so with a big-endian guest the order has been reversed, + * reverse it again + * virtio-1.0 is always little-endian, like PCI, but this + * case will be managed inside qvirtio_is_big_endian() + */ + +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); + uint16_t value; + + value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + value = bswap16(value); + } + return value; } -static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); + uint32_t value; + + value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + value = bswap32(value); + } + return value; } -static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - int i; - uint64_t u64 = 0; + uint64_t val; - if (target_big_endian()) { - for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, - (void *)(uintptr_t)addr + i) << (7 - i) * 8; - } - } else { - for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, - (void *)(uintptr_t)addr + i) << i * 8; - } + val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + val = bswap64(val); } - return u64; + return val; } static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_HOST_FEATURES); + return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES, features); + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_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 + VIRTIO_PCI_GUEST_FEATURES); + return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS); + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS, status); + qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) @@ -152,7 +166,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) } } } else { - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 1; + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1; } } @@ -176,26 +190,26 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) } } } else { - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 2; + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2; } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_SEL, index); + qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NUM); + return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_PFN, pfn); + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); } static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, @@ -247,7 +261,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writew(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NOTIFY, vq->index); + qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_pci = { @@ -286,20 +300,20 @@ 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); + dev->vdev.bus = &qvirtio_pci; + 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); + d->bar = qpci_iomap(d->pdev, 0, NULL); } void qvirtio_pci_device_disable(QVirtioPCIDevice *d) { - qpci_iounmap(d->pdev, d->addr); - d->addr = NULL; + qpci_iounmap(d->pdev, d->bar); } void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, @@ -307,29 +321,33 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, { uint16_t vector; uint32_t control; - void *addr; + uint64_t off; g_assert(d->pdev->msix_enabled); - addr = d->pdev->msix_table + (entry * 16); + off = d->pdev->msix_table_off + (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); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_UPPER_ADDR, + (vqpci->msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); + + control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + 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 + VIRTIO_MSI_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR); + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } @@ -338,10 +356,10 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, { uint16_t vector; uint32_t control; - void *addr; + uint64_t off; g_assert(d->pdev->msix_enabled); - addr = d->pdev->msix_table + (entry * 16); + off = d->pdev->msix_table_off + (entry * 16); g_assert_cmpint(entry, >=, 0); g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); @@ -350,17 +368,21 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, 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 + VIRTIO_MSI_CONFIG_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_UPPER_ADDR, + (d->config_msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_DATA, d->config_msix_data); + + control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index efcac2d3de..38c54c63ea 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -16,7 +16,7 @@ typedef struct QVirtioPCIDevice { QVirtioDevice vdev; QPCIDevice *pdev; - void *addr; + QPCIBar bar; uint16_t config_msix_entry; uint64_t config_msix_addr; uint32_t config_msix_data; diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 105bccecaa..ec30cb99b2 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -13,45 +13,40 @@ #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" -uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) { - return bus->config_readb(d, addr); + return d->bus->config_readb(d, addr); } -uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr) { - return bus->config_readw(d, addr); + return d->bus->config_readw(d, addr); } -uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr) { - return bus->config_readl(d, addr); + return d->bus->config_readl(d, addr); } -uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr) { - return bus->config_readq(d, addr); + return d->bus->config_readq(d, addr); } -uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d) +uint32_t qvirtio_get_features(QVirtioDevice *d) { - return bus->get_features(d); + return d->bus->get_features(d); } -void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, - uint32_t features) +void qvirtio_set_features(QVirtioDevice *d, uint32_t features) { - bus->set_features(d, features); + d->bus->set_features(d, features); } -QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index) +QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) { - return bus->virtqueue_setup(d, alloc, index); + return d->bus->virtqueue_setup(d, alloc, index); } void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, @@ -60,40 +55,40 @@ void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, return bus->virtqueue_cleanup(vq, alloc); } -void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_reset(QVirtioDevice *d) { - bus->set_status(d, 0); - g_assert_cmphex(bus->get_status(d), ==, 0); + d->bus->set_status(d, 0); + g_assert_cmphex(d->bus->get_status(d), ==, 0); } -void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_acknowledge(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); - g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_driver(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); - g_assert_cmphex(bus->get_status(d), ==, + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_driver_ok(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); - g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, +void qvirtio_wait_queue_isr(QVirtioDevice *d, QVirtQueue *vq, gint64 timeout_us) { gint64 start_time = g_get_monotonic_time(); for (;;) { clock_step(100); - if (bus->get_queue_isr_status(d, vq)) { + if (d->bus->get_queue_isr_status(d, vq)) { return; } g_assert(g_get_monotonic_time() - start_time <= timeout_us); @@ -105,8 +100,7 @@ void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, * The virtqueue interrupt must not be raised, making this useful for testing * event_index functionality. */ -uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, - QVirtioDevice *d, +uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d, QVirtQueue *vq, uint64_t addr, gint64 timeout_us) @@ -116,20 +110,19 @@ uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, while ((val = readb(addr)) == 0xff) { clock_step(100); - g_assert(!bus->get_queue_isr_status(d, vq)); + g_assert(!d->bus->get_queue_isr_status(d, vq)); g_assert(g_get_monotonic_time() - start_time <= timeout_us); } return val; } -void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, - gint64 timeout_us) +void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us) { gint64 start_time = g_get_monotonic_time(); for (;;) { clock_step(100); - if (bus->get_config_isr_status(d)) { + if (d->bus->get_config_isr_status(d)) { return; } g_assert(g_get_monotonic_time() - start_time <= timeout_us); @@ -253,8 +246,7 @@ uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *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) +void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head) { /* vq->avail->idx */ uint16_t idx = readw(vq->avail + 2); @@ -276,7 +268,7 @@ void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, /* < 1 because we add elements to avail queue one by one */ if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && (!vq->event || (uint16_t)(idx-avail_event) < 1)) { - bus->virtqueue_kick(d, vq); + d->bus->virtqueue_kick(d, vq); } } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 0250842bf2..3397a080e9 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -15,7 +15,10 @@ #define QVIRTIO_F_BAD_FEATURE 0x40000000 +typedef struct QVirtioBus QVirtioBus; + typedef struct QVirtioDevice { + const QVirtioBus *bus; /* Device type */ uint16_t device_type; } QVirtioDevice; @@ -39,7 +42,7 @@ typedef struct QVRingIndirectDesc { uint16_t elem; } QVRingIndirectDesc; -typedef struct QVirtioBus { +struct QVirtioBus { uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr); uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr); uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr); @@ -84,7 +87,13 @@ typedef struct QVirtioBus { /* Notify changes in virtqueue */ void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq); -} QVirtioBus; +}; + +static inline bool qvirtio_is_big_endian(QVirtioDevice *d) +{ + /* FIXME: virtio 1.0 is always little-endian */ + return qtest_big_endian(global_qtest); +} static inline uint32_t qvring_size(uint32_t num, uint32_t align) { @@ -93,34 +102,27 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align) + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num; } -uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t 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); - -void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, +uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr); +uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr); +uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr); +uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr); +uint32_t qvirtio_get_features(QVirtioDevice *d); +void qvirtio_set_features(QVirtioDevice *d, uint32_t features); + +void qvirtio_reset(QVirtioDevice *d); +void qvirtio_set_acknowledge(QVirtioDevice *d); +void qvirtio_set_driver(QVirtioDevice *d); +void qvirtio_set_driver_ok(QVirtioDevice *d); + +void qvirtio_wait_queue_isr(QVirtioDevice *d, QVirtQueue *vq, gint64 timeout_us); -uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, - QVirtioDevice *d, +uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d, QVirtQueue *vq, uint64_t addr, gint64 timeout_us); -void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, - gint64 timeout_us); -QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index); +void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us); +QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index); void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, QGuestAllocator *alloc); @@ -132,8 +134,7 @@ void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data, 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_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head); void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); #endif diff --git a/tests/libqtest.h b/tests/libqtest.h index 4be1f77877..0224f06d65 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -881,16 +881,6 @@ static inline int64_t clock_set(int64_t val) return qtest_clock_set(global_qtest, val); } -/** - * target_big_endian: - * - * Returns: True if the architecture under test has a big endian configuration. - */ -static inline bool target_big_endian(void) -{ - return qtest_big_endian(global_qtest); -} - QDict *qmp_fd_receive(int fd); void qmp_fd_sendv(int fd, const char *fmt, va_list ap); void qmp_fd_send(int fd, const char *fmt, ...); diff --git a/tests/postcopy-test.c b/tests/postcopy-test.c index 41ed1a976f..d6613c5fa4 100644 --- a/tests/postcopy-test.c +++ b/tests/postcopy-test.c @@ -18,7 +18,7 @@ #include "qemu/sockets.h" #include "sysemu/char.h" #include "sysemu/sysemu.h" -#include "hw/nvram/openbios_firmware_abi.h" +#include "hw/nvram/chrp_nvram.h" #define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */ @@ -137,15 +137,15 @@ static void init_bootfile_ppc(const char *bootpath) { FILE *bootfile; char buf[MIN_NVRAM_SIZE]; - struct OpenBIOS_nvpart_v1 *header = (struct OpenBIOS_nvpart_v1 *)buf; + ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf; memset(buf, 0, MIN_NVRAM_SIZE); /* Create a "common" partition in nvram to store boot-command property */ - header->signature = OPENBIOS_PART_SYSTEM; + header->signature = CHRP_NVPART_SYSTEM; memcpy(header->name, "common", 6); - OpenBIOS_finish_partition(header, MIN_NVRAM_SIZE); + chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE); /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB, * so let's modify memory between 1MB and 100MB diff --git a/tests/prom-env-test.c b/tests/prom-env-test.c index 7a628574c3..0ba6f48607 100644 --- a/tests/prom-env-test.c +++ b/tests/prom-env-test.c @@ -9,11 +9,12 @@ * This work is licensed under the terms of the GNU GPL, version 2 * or later. See the COPYING file in the top-level directory. * - * This test is used to check that some OpenBIOS machines can be started - * successfully in TCG mode. To do this, we first put some Forth code into - * the "boot-command" Open Firmware environment variable. This Forth code - * writes a well-known magic value to a known location in memory. Then we - * start the guest so that OpenBIOS can boot and finally run the Forth code. + * This test is used to check that some Open Firmware based machines (i.e. + * OpenBIOS or SLOF) can be started successfully in TCG mode. To do this, we + * first put some Forth code into the "boot-command" Open Firmware environment + * variable. This Forth code writes a well-known magic value to a known location + * in memory. Then we start the guest so that the firmware can boot and finally + * run the Forth code. * The testing code here then can finally check whether the value has been * successfully written into the guest memory. */ @@ -71,13 +72,16 @@ int main(int argc, char *argv[]) { const char *sparc_machines[] = { "SPARCbook", "Voyager", "SS-20", NULL }; const char *sparc64_machines[] = { "sun4u", "sun4v", NULL }; - const char *mac_machines[] = { "mac99", "g3beige", NULL }; + const char *ppc_machines[] = { "mac99", "g3beige", NULL }; + const char *ppc64_machines[] = { "mac99", "g3beige", "pseries", NULL }; const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); - if (!strcmp(arch, "ppc") || !strcmp(arch, "ppc64")) { - add_tests(mac_machines); + if (!strcmp(arch, "ppc")) { + add_tests(ppc_machines); + } else if (!strcmp(arch, "ppc64")) { + add_tests(ppc64_machines); } else if (!strcmp(arch, "sparc")) { add_tests(sparc_machines); } else if (!strcmp(arch, "sparc64")) { diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 408d613bc1..42bf4164ca 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -222,7 +222,7 @@ Testing: -drive driver=file QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name Testing: -drive driver=nbd -QEMU_PROG: -drive driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive driver=nbd: NBD server address missing Testing: -drive driver=raw QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level @@ -231,7 +231,7 @@ Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name Testing: -drive file.driver=nbd -QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive file.driver=nbd: NBD server address missing Testing: -drive file.driver=raw QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index ec6d22229c..603bb768d6 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -316,7 +316,7 @@ Testing: -drive driver=file QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name Testing: -drive driver=nbd -QEMU_PROG: -drive driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive driver=nbd: NBD server address missing Testing: -drive driver=raw QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level @@ -325,7 +325,7 @@ Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name Testing: -drive file.driver=nbd -QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive file.driver=nbd: NBD server address missing Testing: -drive file.driver=raw QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 new file mode 100755 index 0000000000..45469c911e --- /dev/null +++ b/tests/qemu-iotests/147 @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# Test case for NBD's blockdev-add interface +# +# Copyright (C) 2016 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import socket +import stat +import time +import iotests +from iotests import cachemode, imgfmt, qemu_img, qemu_nbd + +NBD_PORT = 10811 + +test_img = os.path.join(iotests.test_dir, 'test.img') +unix_socket = os.path.join(iotests.test_dir, 'nbd.socket') + +class NBDBlockdevAddBase(iotests.QMPTestCase): + def blockdev_add_options(self, address, export=None): + options = { 'node-name': 'nbd-blockdev', + 'driver': 'raw', + 'file': { + 'driver': 'nbd', + 'server': address + } } + if export is not None: + options['file']['export'] = export + return options + + def client_test(self, filename, address, export=None): + bao = self.blockdev_add_options(address, export) + result = self.vm.qmp('blockdev-add', **bao) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-named-block-nodes') + for node in result['return']: + if node['node-name'] == 'nbd-blockdev': + if isinstance(filename, str): + self.assert_qmp(node, 'image/filename', filename) + else: + self.assert_json_filename_equal(node['image']['filename'], + filename) + break + + result = self.vm.qmp('x-blockdev-del', node_name='nbd-blockdev') + self.assert_qmp(result, 'return', {}) + + +class QemuNBD(NBDBlockdevAddBase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + try: + os.remove(unix_socket) + except OSError: + pass + + def _server_up(self, *args): + self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) + + def test_inet(self): + self._server_up('-p', str(NBD_PORT)) + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(NBD_PORT) + } } + self.client_test('nbd://localhost:%i' % NBD_PORT, address) + + def test_unix(self): + self._server_up('-k', unix_socket) + address = { 'type': 'unix', + 'data': { 'path': unix_socket } } + self.client_test('nbd+unix://?socket=' + unix_socket, address) + + +class BuiltinNBD(NBDBlockdevAddBase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') + self.vm = iotests.VM() + self.vm.launch() + self.server = iotests.VM('.server') + self.server.add_drive_raw('if=none,id=nbd-export,' + + 'file=%s,' % test_img + + 'format=%s,' % imgfmt + + 'cache=%s' % cachemode) + self.server.launch() + + def tearDown(self): + self.vm.shutdown() + self.server.shutdown() + os.remove(test_img) + try: + os.remove(unix_socket) + except OSError: + pass + + def _server_up(self, address): + result = self.server.qmp('nbd-server-start', addr=address) + self.assert_qmp(result, 'return', {}) + + result = self.server.qmp('nbd-server-add', device='nbd-export') + self.assert_qmp(result, 'return', {}) + + def _server_down(self): + result = self.server.qmp('nbd-server-stop') + self.assert_qmp(result, 'return', {}) + + def test_inet(self): + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(NBD_PORT) + } } + self._server_up(address) + self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT, + address, 'nbd-export') + self._server_down() + + def test_inet6(self): + address = { 'type': 'inet', + 'data': { + 'host': '::1', + 'port': str(NBD_PORT), + 'ipv4': False, + 'ipv6': True + } } + filename = { 'driver': 'raw', + 'file': { + 'driver': 'nbd', + 'export': 'nbd-export', + 'server': address + } } + self._server_up(address) + self.client_test(filename, address, 'nbd-export') + self._server_down() + + def test_unix(self): + address = { 'type': 'unix', + 'data': { 'path': unix_socket } } + self._server_up(address) + self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket, + address, 'nbd-export') + self._server_down() + + def test_fd(self): + self._server_up({ 'type': 'unix', + 'data': { 'path': unix_socket } }) + + sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sockfd.connect(unix_socket) + + result = self.vm.send_fd_scm(str(sockfd.fileno())) + self.assertEqual(result, 0, 'Failed to send socket FD') + + result = self.vm.qmp('getfd', fdname='nbd-fifo') + self.assert_qmp(result, 'return', {}) + + address = { 'type': 'fd', + 'data': { 'str': 'nbd-fifo' } } + filename = { 'driver': 'raw', + 'file': { + 'driver': 'nbd', + 'export': 'nbd-export', + 'server': address + } } + self.client_test(filename, address, 'nbd-export') + + self._server_down() + + +if __name__ == '__main__': + # Need to support image creation + iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2', + 'vmdk', 'raw', 'vhdx', 'qed']) diff --git a/tests/qemu-iotests/147.out b/tests/qemu-iotests/147.out new file mode 100644 index 0000000000..3f8a935a08 --- /dev/null +++ b/tests/qemu-iotests/147.out @@ -0,0 +1,5 @@ +...... +---------------------------------------------------------------------- +Ran 6 tests + +OK diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 new file mode 100755 index 0000000000..1b7d3a194d --- /dev/null +++ b/tests/qemu-iotests/172 @@ -0,0 +1,246 @@ +#!/bin/bash +# +# Test floppy configuration +# +# Copyright (C) 2016 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then + _notrun "Requires a PC machine" +fi + +function do_run_qemu() +{ + ( + if ! test -t 0; then + while read cmd; do + echo $cmd + done + fi + echo quit + ) | $QEMU -nographic -monitor stdio -serial none "$@" + echo +} + +function check_floppy_qtree() +{ + echo + echo Testing: "$@" | _filter_testdir + + # QEMU_OPTIONS contains -nodefaults, we don't want that here because the + # defaults are part of what should be checked here. + # + # Apply the sed filter to stdout only, but keep the stderr output and + # filter the qemu program name in it. + echo "info qtree" | + (QEMU_OPTIONS="" do_run_qemu "$@" | + sed -ne '/^ dev: isa-fdc/,/^ dev:/{x;p}' ) 2>&1 | + _filter_win32 | _filter_qemu +} + +function check_cache_mode() +{ + echo "info block none0" | + QEMU_OPTIONS="" do_run_qemu -drive if=none,file="$TEST_IMG" "$@" | + _filter_win32 | _filter_qemu | grep "Cache mode" +} + + +size=720k + +_make_test_img $size + +# Default drive semantics: +# +# By default you get a single empty floppy drive. You can override it with +# -drive and using the same index, but if you use -drive to add a floppy to a +# different index, you get both of them. However, as soon as you use any +# '-device floppy', even to a different slot, the default drive is disabled. + +echo +echo +echo === Default === + +check_floppy_qtree + +echo +echo +echo === Using -fda/-fdb options === + +check_floppy_qtree -fda "$TEST_IMG" +check_floppy_qtree -fdb "$TEST_IMG" +check_floppy_qtree -fda "$TEST_IMG" -fdb "$TEST_IMG" + + +echo +echo +echo === Using -drive options === + +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" +check_floppy_qtree -drive if=floppy,file="$TEST_IMG",index=1 +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=floppy,file="$TEST_IMG",index=1 + +echo +echo +echo === Using -drive if=none and -global === + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1 + +echo +echo +echo === Using -drive if=none and -device === + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -device floppy,drive=none0 -device floppy,drive=none1,unit=1 + +echo +echo +echo === Mixing -fdX and -global === + +# Working +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0 + +# Conflicting (-fdX wins) +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0 + +echo +echo +echo === Mixing -fdX and -device === + +# Working +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 + +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0 + +# Conflicting +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 + +echo +echo +echo === Mixing -drive and -device === + +# Working +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 + +# Conflicting +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0 + +echo +echo +echo === Mixing -global and -device === + +# Working +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -device floppy,drive=none1 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1 + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 -device floppy,drive=none1 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0 + +# Conflicting +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1 + +echo +echo +echo === Too many floppy drives === + +# Working +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" \ + -drive if=none,file="$TEST_IMG" \ + -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 \ + -device floppy,drive=none1 + +echo +echo +echo === Creating an empty drive with anonymous BB === + +check_floppy_qtree -device floppy +check_floppy_qtree -device floppy,drive-type=120 +check_floppy_qtree -device floppy,drive-type=144 +check_floppy_qtree -device floppy,drive-type=288 + +echo +echo +echo === Try passing different drive size with image === + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,drive-type=120 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,drive-type=288 + +echo +echo +echo === Try passing different block sizes === + +# Explicitly setting the default is okay +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,logical_block_size=512 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,physical_block_size=512 + +# Changing it is not +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,logical_block_size=4096 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,physical_block_size=1024 + +echo +echo +echo === Writethrough caching === + +check_cache_mode -device floppy,drive=none0 +check_cache_mode -device floppy,drive=none0,write-cache=on +check_cache_mode -device floppy,drive=none0,write-cache=off + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out new file mode 100644 index 0000000000..6b7edaf28f --- /dev/null +++ b/tests/qemu-iotests/172.out @@ -0,0 +1,1170 @@ +QA output created by 172 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=737280 + + +=== Default === + +Testing: + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + + +=== Using -fda/-fdb options === + +Testing: -fda TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + +Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Using -drive options === + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Using -drive if=none and -global === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Using -drive if=none and -device === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Mixing -fdX and -global === + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Mixing -fdX and -device === + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0 +QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use +QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed. + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 +QEMU_PROG: -device floppy,drive=none0,unit=1: Floppy unit 1 is in use +QEMU_PROG: -device floppy,drive=none0,unit=1: Device initialization failed. + + +=== Mixing -drive and -device === + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0 +QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use +QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed. + + +=== Mixing -global and -device === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0 +QEMU_PROG: -device floppy,drive=none1,unit=0: Floppy unit 0 is in use +QEMU_PROG: -device floppy,drive=none1,unit=0: Device initialization failed. + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1 +QEMU_PROG: -device floppy,drive=none1,unit=1: Floppy unit 1 is in use +QEMU_PROG: -device floppy,drive=none1,unit=1: Device initialization failed. + + +=== Too many floppy drives === + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1 +QEMU_PROG: -device floppy,drive=none1: Can't create floppy unit 2, bus supports only 2 units +QEMU_PROG: -device floppy,drive=none1: Device initialization failed. + + +=== Creating an empty drive with anonymous BB === + +Testing: -device floppy + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + +Testing: -device floppy,drive-type=120 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "120" + +Testing: -device floppy,drive-type=144 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -device floppy,drive-type=288 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + + +=== Try passing different drive size with image === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-type=120 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "120" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-type=288 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + + +=== Try passing different block sizes === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical_block_size=512 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physical_block_size=512 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical_block_size=4096 +QEMU_PROG: -device floppy,drive=none0,logical_block_size=4096: Physical and logical block size must be 512 for floppy +QEMU_PROG: -device floppy,drive=none0,logical_block_size=4096: Device initialization failed. + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physical_block_size=1024 +QEMU_PROG: -device floppy,drive=none0,physical_block_size=1024: Physical and logical block size must be 512 for floppy +QEMU_PROG: -device floppy,drive=none0,physical_block_size=1024: Device initialization failed. + + +=== Writethrough caching === + Cache mode: writeback + Cache mode: writeback + Cache mode: writethrough +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 126bd67043..3213765f4e 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -69,7 +69,7 @@ if [ "$IMGOPTSSYNTAX" = "true" ]; then TEST_IMG="$DRIVER,file.driver=ssh,file.host=127.0.0.1,file.path=$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then TEST_DIR="$DRIVER,file.driver=nfs,file.filename=nfs://127.0.0.1/$TEST_DIR" - TEST_IMG=$TEST_DIR_OPTS/t.$IMGFMT + TEST_IMG=$TEST_DIR/t.$IMGFMT elif [ "$IMGPROTO" = "archipelago" ]; then TEST_IMG="$DRIVER,file.driver=archipelago,file.volume=:at.$IMGFMT" else diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 7eb17707a2..597fc2c952 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -149,6 +149,7 @@ 144 rw auto quick 145 auto quick 146 auto quick +147 auto 148 rw auto quick 149 rw auto sudo 150 rw auto quick @@ -162,3 +163,4 @@ 160 rw auto quick 162 auto quick 170 rw auto quick +172 auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 3329bc1721..1f30cfcc75 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -39,6 +39,10 @@ qemu_io_args = [os.environ.get('QEMU_IO_PROG', 'qemu-io')] if os.environ.get('QEMU_IO_OPTIONS'): qemu_io_args += os.environ['QEMU_IO_OPTIONS'].strip().split(' ') +qemu_nbd_args = [os.environ.get('QEMU_NBD_PROG', 'qemu-nbd')] +if os.environ.get('QEMU_NBD_OPTIONS'): + qemu_nbd_args += os.environ['QEMU_NBD_OPTIONS'].strip().split(' ') + qemu_prog = os.environ.get('QEMU_PROG', 'qemu') qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ') @@ -87,6 +91,10 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] +def qemu_nbd(*args): + '''Run qemu-nbd in daemon mode and return the parent's exit code''' + return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) + def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): '''Return True if two image files are identical''' return qemu_img('compare', '-f', fmt1, @@ -132,8 +140,10 @@ def log(msg, filters=[]): class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' - def __init__(self): - super(VM, self).__init__(qemu_prog, qemu_opts, test_dir=test_dir, + def __init__(self, path_suffix=''): + name = "qemu%s-%d" % (path_suffix, os.getpid()) + super(VM, self).__init__(qemu_prog, qemu_opts, name=name, + test_dir=test_dir, socket_scm_helper=socket_scm_helper) if debug: self._debug = True @@ -212,6 +222,19 @@ class QMPTestCase(unittest.TestCase): self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) return d + def flatten_qmp_object(self, obj, output=None, basestr=''): + if output is None: + output = dict() + if isinstance(obj, list): + for i in range(len(obj)): + self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') + elif isinstance(obj, dict): + for key in obj: + self.flatten_qmp_object(obj[key], output, basestr + key + '.') + else: + output[basestr[:-1]] = obj # Strip trailing '.' + return output + def assert_qmp_absent(self, d, path): try: result = self.dictpath(d, path) @@ -242,6 +265,13 @@ class QMPTestCase(unittest.TestCase): self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \ (node_name, file_name, result)) + def assert_json_filename_equal(self, json_filename, reference): + '''Asserts that the given filename is a json: filename and that its + content is equal to the given reference object''' + self.assertEqual(json_filename[:5], 'json:') + self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])), + self.flatten_qmp_object(reference)) + def cancel_and_wait(self, drive='drive0', force=False, resume=False): '''Cancel a block job and wait for it to finish, returning the event''' result = self.vm.qmp('block-job-cancel', device=drive, force=force) diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c index 80cadf43bc..eb76d31aa9 100644 --- a/tests/qemu-iotests/socket_scm_helper.c +++ b/tests/qemu-iotests/socket_scm_helper.c @@ -60,7 +60,7 @@ static int send_fd(int fd, int fd_to_send) } /* Convert string to fd number. */ -static int get_fd_num(const char *fd_str) +static int get_fd_num(const char *fd_str, bool silent) { int sock; char *err; @@ -68,12 +68,16 @@ static int get_fd_num(const char *fd_str) errno = 0; sock = strtol(fd_str, &err, 10); if (errno) { - fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", - strerror(errno)); + if (!silent) { + fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", + strerror(errno)); + } return -1; } if (!*fd_str || *err || sock < 0) { - fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); + if (!silent) { + fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); + } return -1; } @@ -104,18 +108,21 @@ int main(int argc, char **argv, char **envp) } - sock = get_fd_num(argv[1]); + sock = get_fd_num(argv[1], false); if (sock < 0) { return EXIT_FAILURE; } - /* Now only open a file in readonly mode for test purpose. If more precise - control is needed, use python script in file operation, which is - supposed to fork and exec this program. */ - fd = open(argv[2], O_RDONLY); + fd = get_fd_num(argv[2], true); if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'\n", argv[2]); - return EXIT_FAILURE; + /* Now only open a file in readonly mode for test purpose. If more + precise control is needed, use python script in file operation, which + is supposed to fork and exec this program. */ + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'\n", argv[2]); + return EXIT_FAILURE; + } } ret = send_fd(sock, fd); diff --git a/tests/rtas-test.c b/tests/rtas-test.c index ba0867afbd..276c87ef84 100644 --- a/tests/rtas-test.c +++ b/tests/rtas-test.c @@ -14,7 +14,6 @@ static void test_rtas_get_time_of_day(void) time_t t1, t2; qs = qtest_spapr_boot("-machine pseries"); - g_assert(qs != NULL); t1 = time(NULL); ret = qrtas_get_time_of_day(qs->alloc, &tm, &ns); diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index c2f601a380..7de7dc45ae 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -22,7 +22,7 @@ static void nop(void) static QPCIBus *pcibus; static QPCIDevice *dev; -static void *dev_base; +static QPCIBar dev_bar; static void save_fn(QPCIDevice *dev, int devfn, void *data) { @@ -45,14 +45,14 @@ static QPCIDevice *get_device(void) #define PORT(name, len, val) \ static unsigned __attribute__((unused)) in_##name(void) \ { \ - unsigned res = qpci_io_read##len(dev, dev_base+(val)); \ + unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ g_test_message("*%s -> %x\n", #name, res); \ return res; \ } \ static void out_##name(unsigned v) \ { \ g_test_message("%x -> *%s\n", v, #name); \ - qpci_io_write##len(dev, dev_base+(val), v); \ + qpci_io_write##len(dev, dev_bar, (val), v); \ } PORT(Timer, l, 0x48) @@ -186,9 +186,7 @@ static void test_init(void) dev = get_device(); - dev_base = qpci_iomap(dev, 0, &barsize); - - g_assert(dev_base != NULL); + dev_bar = qpci_iomap(dev, 0, &barsize); qpci_device_enable(dev); diff --git a/tests/tco-test.c b/tests/tco-test.c index 0d201b1fcb..ef02ec5903 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -41,7 +41,7 @@ typedef struct { const char *args; bool noreboot; QPCIDevice *dev; - void *tco_io_base; + QPCIBar tco_io_bar; } TestData; static void test_init(TestData *d) @@ -70,42 +70,42 @@ static void test_init(TestData *d) /* set Root Complex BAR */ qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1); - d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60); + d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60); } static void stop_tco(const TestData *d) { uint32_t val; - val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT); + val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); val |= TCO_TMR_HLT; - qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val); + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); } static void start_tco(const TestData *d) { uint32_t val; - val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT); + val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); val &= ~TCO_TMR_HLT; - qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val); + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); } static void load_tco(const TestData *d) { - qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4); + qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4); } static void set_tco_timeout(const TestData *d, uint16_t ticks) { - qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks); + qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks); } static void clear_tco_status(const TestData *d) { - qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008); - qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002); - qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004); + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008); + qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002); + qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004); } static void reset_on_second_timeout(bool enable) @@ -128,25 +128,25 @@ static void test_tco_defaults(void) d.args = NULL; d.noreboot = true; test_init(&d); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==, TCO_RLD_DEFAULT); /* TCO_DAT_IN & TCO_DAT_OUT */ - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==, (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT); /* TCO1_STS & TCO2_STS */ - g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==, + g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==, (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT); /* TCO1_CNT & TCO2_CNT */ - g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==, + g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==, (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT); /* TCO_MESSAGE1 & TCO_MESSAGE2 */ - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==, (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT); - g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==, + g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==, TCO_WDCNT_DEFAULT); - g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==, + g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==, SW_IRQ_GEN_DEFAULT); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==, TCO_TMR_DEFAULT); qtest_end(); } @@ -171,23 +171,23 @@ static void test_tco_timeout(void) clock_step(ticks * TCO_TICK_NSEC); /* test first timeout */ - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); /* test clearing timeout bit */ val |= TCO_TIMEOUT; - qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 0); /* test second timeout */ clock_step(ticks * TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); ret = val & TCO_SECOND_TO_STS ? 1 : 0; g_assert(ret == 1); @@ -214,13 +214,13 @@ static void test_tco_max_timeout(void) start_tco(&d); clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD); g_assert_cmpint(val & TCO_RLD_MASK, ==, 1); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 0); clock_step(TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); @@ -358,11 +358,11 @@ static void test_tco_ticks_counter(void) start_tco(&d); do { - rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK; + rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK; g_assert_cmpint(rld, ==, ticks); clock_step(TCO_TICK_NSEC); ticks--; - } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT)); + } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT)); stop_tco(&d); qtest_end(); @@ -378,10 +378,10 @@ static void test_tco1_control_bits(void) test_init(&d); val = TCO_LOCK; - qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); val &= ~TCO_LOCK; - qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==, + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==, TCO_LOCK); qtest_end(); } @@ -405,13 +405,13 @@ static void test_tco1_status_bits(void) start_tco(&d); clock_step(ticks * TCO_TICK_NSEC); - qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0); - qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0); + qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0; g_assert(ret == 1); - qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0); qtest_end(); } @@ -434,11 +434,11 @@ static void test_tco2_status_bits(void) start_tco(&d); clock_step(ticks * TCO_TICK_NSEC * 2); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0; g_assert(ret == 1); - qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0); + qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0); qtest_end(); } diff --git a/tests/test-aio.c b/tests/test-aio.c index 03aa846970..5be99f8287 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -100,6 +100,7 @@ static void event_ready_cb(EventNotifier *e) typedef struct { QemuMutex start_lock; + EventNotifier notifier; bool thread_acquired; } AcquireTestData; @@ -111,6 +112,11 @@ static void *test_acquire_thread(void *opaque) qemu_mutex_lock(&data->start_lock); qemu_mutex_unlock(&data->start_lock); + /* event_notifier_set might be called either before or after + * the main thread's call to poll(). The test case's outcome + * should be the same in either case. + */ + event_notifier_set(&data->notifier); aio_context_acquire(ctx); aio_context_release(ctx); @@ -125,20 +131,19 @@ static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, aio_set_event_notifier(ctx, notifier, false, handler); } -static void dummy_notifier_read(EventNotifier *unused) +static void dummy_notifier_read(EventNotifier *n) { - g_assert(false); /* should never be invoked */ + event_notifier_test_and_clear(n); } static void test_acquire(void) { QemuThread thread; - EventNotifier notifier; AcquireTestData data; /* Dummy event notifier ensures aio_poll() will block */ - event_notifier_init(¬ifier, false); - set_event_notifier(ctx, ¬ifier, dummy_notifier_read); + event_notifier_init(&data.notifier, false); + set_event_notifier(ctx, &data.notifier, dummy_notifier_read); g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ qemu_mutex_init(&data.start_lock); @@ -152,12 +157,13 @@ static void test_acquire(void) /* Block in aio_poll(), let other thread kick us and acquire context */ aio_context_acquire(ctx); qemu_mutex_unlock(&data.start_lock); /* let the thread run */ - g_assert(!aio_poll(ctx, true)); + g_assert(aio_poll(ctx, true)); + g_assert(!data.thread_acquired); aio_context_release(ctx); qemu_thread_join(&thread); - set_event_notifier(ctx, ¬ifier, NULL); - event_notifier_cleanup(¬ifier); + set_event_notifier(ctx, &data.notifier, NULL); + event_notifier_cleanup(&data.notifier); g_assert(data.thread_acquired); } diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c index f73e063d7d..aa88c3cf45 100644 --- a/tests/test-io-channel-socket.c +++ b/tests/test-io-channel-socket.c @@ -491,6 +491,37 @@ static void test_io_channel_unix_fd_pass(void) } g_free(fdrecv); } + +static void test_io_channel_unix_listen_cleanup(void) +{ + QIOChannelSocket *ioc; + struct sockaddr_un un; + int sock; + +#define TEST_SOCKET "test-io-channel-socket.sock" + + ioc = qio_channel_socket_new(); + + /* Manually bind ioc without calling the qio api to avoid setting + * the LISTEN feature */ + sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); + memset(&un, 0, sizeof(un)); + un.sun_family = AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET); + unlink(TEST_SOCKET); + bind(sock, (struct sockaddr *)&un, sizeof(un)); + ioc->fd = sock; + ioc->localAddrLen = sizeof(ioc->localAddr); + getsockname(sock, (struct sockaddr *)&ioc->localAddr, + &ioc->localAddrLen); + + g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); + object_unref(OBJECT(ioc)); + g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); + + unlink(TEST_SOCKET); +} + #endif /* _WIN32 */ @@ -562,6 +593,8 @@ int main(int argc, char **argv) test_io_channel_unix_async); g_test_add_func("/io/channel/socket/unix-fd-pass", test_io_channel_unix_fd_pass); + g_test_add_func("/io/channel/socket/unix-listen-cleanup", + test_io_channel_unix_listen_cleanup); #endif /* _WIN32 */ return g_test_run(); diff --git a/tests/test-rfifolock.c b/tests/test-rfifolock.c deleted file mode 100644 index 471a81114d..0000000000 --- a/tests/test-rfifolock.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * RFifoLock tests - * - * Copyright Red Hat, Inc. 2013 - * - * Authors: - * Stefan Hajnoczi <stefanha@redhat.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/rfifolock.h" - -static void test_nesting(void) -{ - RFifoLock lock; - - /* Trivial test, ensure the lock is recursive */ - rfifolock_init(&lock, NULL, NULL); - rfifolock_lock(&lock); - rfifolock_lock(&lock); - rfifolock_lock(&lock); - rfifolock_unlock(&lock); - rfifolock_unlock(&lock); - rfifolock_unlock(&lock); - rfifolock_destroy(&lock); -} - -typedef struct { - RFifoLock lock; - int fd[2]; -} CallbackTestData; - -static void rfifolock_cb(void *opaque) -{ - CallbackTestData *data = opaque; - int ret; - char c = 0; - - ret = write(data->fd[1], &c, sizeof(c)); - g_assert(ret == 1); -} - -static void *callback_thread(void *opaque) -{ - CallbackTestData *data = opaque; - - /* The other thread holds the lock so the contention callback will be - * invoked... - */ - rfifolock_lock(&data->lock); - rfifolock_unlock(&data->lock); - return NULL; -} - -static void test_callback(void) -{ - CallbackTestData data; - QemuThread thread; - int ret; - char c; - - rfifolock_init(&data.lock, rfifolock_cb, &data); - ret = qemu_pipe(data.fd); - g_assert(ret == 0); - - /* Hold lock but allow the callback to kick us by writing to the pipe */ - rfifolock_lock(&data.lock); - qemu_thread_create(&thread, "callback_thread", - callback_thread, &data, QEMU_THREAD_JOINABLE); - ret = read(data.fd[0], &c, sizeof(c)); - g_assert(ret == 1); - rfifolock_unlock(&data.lock); - /* If we got here then the callback was invoked, as expected */ - - qemu_thread_join(&thread); - close(data.fd[0]); - close(data.fd[1]); - rfifolock_destroy(&data.lock); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/nesting", test_nesting); - g_test_add_func("/callback", test_callback); - return g_test_run(); -} diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index a4ceeaaa43..57af8a034e 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -38,8 +38,7 @@ static void uhci_port_update(struct qhc *hc, int port, static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) { - void *addr = hc->base + 0x64 + 4 * port; - uint32_t value = qpci_io_readl(hc->dev, addr); + uint32_t value = qpci_io_readl(hc->dev, hc->bar, 0x64 + 4 * port); uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC); #if 0 @@ -91,7 +90,7 @@ static void pci_ehci_port_1(void) static void pci_ehci_config(void) { /* hands over all ports from companion uhci to ehci */ - qpci_io_writew(ehci1.dev, ehci1.base + 0x60, 1); + qpci_io_writew(ehci1.dev, ehci1.bar, 0x60, 1); } static void pci_uhci_port_2(void) diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index a7f06291cb..96bf00eefa 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -22,8 +22,6 @@ #include "libqos/virtio-pci.h" #include "qapi/error.h" -#include "libqos/pci-pc.h" -#include "libqos/virtio-pci.h" #include "libqos/malloc-pc.h" #include "hw/virtio/virtio-net.h" @@ -172,15 +170,15 @@ static void init_virtio_dev(TestServer *s) g_assert_nonnull(dev); 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); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); features = features & VIRTIO_NET_F_MAC; - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); } static void wait_for_fds(TestServer *s) @@ -847,24 +845,24 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); 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); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); return dev; } -static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev) +static void driver_init(QVirtioDevice *dev) { uint32_t features; - features = qvirtio_get_features(bus, dev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(bus, dev, features); + qvirtio_set_features(dev, features); - qvirtio_set_driver_ok(bus, dev); + qvirtio_set_driver_ok(dev); } #define PCI_SLOT 0x04 @@ -896,16 +894,15 @@ static void test_multiqueue(void) alloc = pc_alloc_init(); for (i = 0; i < queues * 2; i++) { - vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, i); + vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, alloc, i); } - driver_init(&qvirtio_pci, &dev->vdev); + driver_init(&dev->vdev); wait_for_rings_started(s, queues * 2); /* End test */ for (i = 0; i < queues * 2; i++) { - qvirtqueue_cleanup(&qvirtio_pci, &vq[i]->vq, alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vq[i]->vq, alloc); } pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index e8b21967d8..9c4f6cb406 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -10,112 +10,111 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qemu-common.h" -#include "libqos/pci-pc.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" static const char mount_tag[] = "qtest"; static char *test_share; -static void qvirtio_9p_start(void) + +static QOSState *qvirtio_9p_start(void) { - char *args; + const char *arch = qtest_get_arch(); + const char *cmd = "-fsdev local,id=fsdev0,security_model=none,path=%s " + "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s"; test_share = g_strdup("/tmp/qtest.XXXXXX"); g_assert_nonnull(mkdtemp(test_share)); - args = g_strdup_printf("-fsdev local,id=fsdev0,security_model=none,path=%s " - "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s", - test_share, mount_tag); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + return qtest_pc_boot(cmd, test_share, mount_tag); + } + if (strcmp(arch, "ppc64") == 0) { + return qtest_spapr_boot(cmd, test_share, mount_tag); + } - qtest_start(args); - g_free(args); + g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } -static void qvirtio_9p_stop(void) +static void qvirtio_9p_stop(QOSState *qs) { - qtest_end(); + qtest_shutdown(qs); rmdir(test_share); g_free(test_share); } static void pci_nop(void) { - qvirtio_9p_start(); - qvirtio_9p_stop(); + QOSState *qs; + + qs = qvirtio_9p_start(); + qvirtio_9p_stop(qs); } typedef struct { QVirtioDevice *dev; - QGuestAllocator *alloc; - QPCIBus *bus; + QOSState *qs; QVirtQueue *vq; } QVirtIO9P; -static QVirtIO9P *qvirtio_9p_pci_init(void) +static QVirtIO9P *qvirtio_9p_pci_init(QOSState *qs) { QVirtIO9P *v9p; QVirtioPCIDevice *dev; v9p = g_new0(QVirtIO9P, 1); - v9p->alloc = pc_alloc_init(); - v9p->bus = qpci_init_pc(NULL); - dev = qvirtio_pci_device_find(v9p->bus, VIRTIO_ID_9P); + v9p->qs = qs; + dev = qvirtio_pci_device_find(v9p->qs->pcibus, VIRTIO_ID_9P); g_assert_nonnull(dev); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P); v9p->dev = (QVirtioDevice *) dev; qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, v9p->dev); - qvirtio_set_acknowledge(&qvirtio_pci, v9p->dev); - qvirtio_set_driver(&qvirtio_pci, v9p->dev); + qvirtio_reset(v9p->dev); + qvirtio_set_acknowledge(v9p->dev); + qvirtio_set_driver(v9p->dev); - v9p->vq = qvirtqueue_setup(&qvirtio_pci, v9p->dev, v9p->alloc, 0); + v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0); return v9p; } static void qvirtio_9p_pci_free(QVirtIO9P *v9p) { - qvirtqueue_cleanup(&qvirtio_pci, v9p->vq, v9p->alloc); - pc_alloc_uninit(v9p->alloc); + qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc); qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev)); g_free(v9p->dev); - qpci_free_pc(v9p->bus); g_free(v9p); } static void pci_basic_config(void) { QVirtIO9P *v9p; - void *addr; size_t tag_len; char *tag; int i; + QOSState *qs; - qvirtio_9p_start(); - v9p = qvirtio_9p_pci_init(); + qs = qvirtio_9p_start(); + v9p = qvirtio_9p_pci_init(qs); - addr = ((QVirtioPCIDevice *) v9p->dev)->addr + VIRTIO_PCI_CONFIG_OFF(false); - tag_len = qvirtio_config_readw(&qvirtio_pci, v9p->dev, - (uint64_t)(uintptr_t)addr); + tag_len = qvirtio_config_readw(v9p->dev, 0); g_assert_cmpint(tag_len, ==, strlen(mount_tag)); - addr += sizeof(uint16_t); tag = g_malloc(tag_len); for (i = 0; i < tag_len; i++) { - tag[i] = qvirtio_config_readb(&qvirtio_pci, v9p->dev, - (uint64_t)(uintptr_t)addr + i); + tag[i] = qvirtio_config_readb(v9p->dev, i + 2); } g_assert_cmpmem(tag, tag_len, mount_tag, tag_len); g_free(tag); qvirtio_9p_pci_free(v9p); - qvirtio_9p_stop(); + qvirtio_9p_stop(qs); } int main(int argc, char **argv) diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 0506917341..0e32e416dd 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -10,12 +10,11 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" #include "libqos/virtio-mmio.h" -#include "libqos/pci-pc.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" #include "libqos/malloc-generic.h" #include "qemu/bswap.h" #include "standard-headers/linux/virtio_ids.h" @@ -58,24 +57,29 @@ static char *drive_create(void) return tmp_path; } -static QPCIBus *pci_test_start(void) +static QOSState *pci_test_start(void) { - char *cmdline; + QOSState *qs; + const char *arch = qtest_get_arch(); char *tmp_path; + const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " + "-drive if=none,id=drive1,file=/dev/null,format=raw " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x"; tmp_path = drive_create(); - cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw " - "-drive if=none,id=drive1,file=/dev/null,format=raw " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x", - tmp_path, PCI_SLOT, PCI_FN); - qtest_start(cmdline); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); + } else if (strcmp(arch, "ppc64") == 0) { + qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); + } else { + g_printerr("virtio-blk tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); + } unlink(tmp_path); g_free(tmp_path); - g_free(cmdline); - - return qpci_init_pc(NULL); + return qs; } static void arm_test_start(void) @@ -110,30 +114,30 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->pdev->devfn, ==, ((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); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); return dev; } -static inline void virtio_blk_fix_request(QVirtioBlkReq *req) +static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) { #ifdef HOST_WORDS_BIGENDIAN - bool host_endian = true; + const bool host_is_big_endian = true; #else - bool host_endian = false; + const bool host_is_big_endian = false; #endif - if (target_big_endian() != host_endian) { + if (qvirtio_is_big_endian(d) != host_is_big_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) +static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, + QVirtioBlkReq *req, uint64_t data_size) { uint64_t addr; uint8_t status = 0xFF; @@ -141,7 +145,7 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, g_assert_cmpuint(data_size % 512, ==, 0); addr = guest_alloc(alloc, sizeof(*req) + data_size); - virtio_blk_fix_request(req); + virtio_blk_fix_request(d, req); memwrite(addr, req, 16); memwrite(addr + 16, req->data, data_size); @@ -150,8 +154,8 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, return addr; } -static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific) +static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, + QVirtQueue *vq) { QVirtioBlkReq req; uint64_t req_addr; @@ -161,18 +165,18 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, uint8_t status; char *data; - capacity = qvirtio_config_readq(bus, dev, device_specific); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(bus, dev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(bus, dev, features); + qvirtio_set_features(dev, features); - qvirtio_set_driver_ok(bus, dev); + qvirtio_set_driver_ok(dev); /* Write and read with 3 descriptor layout */ /* Write request */ @@ -182,7 +186,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); @@ -190,9 +194,9 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, qvirtqueue_add(vq, req_addr + 16, 512, false, true); qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -204,7 +208,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); @@ -212,9 +216,9 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, qvirtqueue_add(vq, req_addr + 16, 512, true, true); qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -234,15 +238,15 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); free_head = qvirtqueue_add(vq, req_addr, 528, false, true); qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -254,16 +258,16 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); free_head = qvirtqueue_add(vq, req_addr, 16, false, true); qvirtqueue_add(vq, req_addr + 16, 513, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -279,42 +283,30 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, static void pci_basic(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; - void *addr; - bus = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + qs = pci_test_start(); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - alloc = pc_alloc_init(); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - /* MSI-X is not enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); - - test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq, - (uint64_t)(uintptr_t)addr); + test_basic(&dev->vdev, qs->alloc, &vqpci->vq); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_indirect(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; + QOSState *qs; QVirtioBlkReq req; QVRingIndirectDesc *indirect; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -322,28 +314,22 @@ static void pci_indirect(void) uint8_t status; char *data; - bus = pci_test_start(); - - dev = virtio_blk_pci_init(bus, PCI_SLOT); + qs = pci_test_start(); - /* MSI-X is not enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&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); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + qvirtio_set_driver_ok(&dev->vdev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -352,23 +338,23 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2); + indirect = qvring_indirect_desc_setup(&dev->vdev, qs->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); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); g_free(indirect); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -377,17 +363,17 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2); + indirect = qvring_indirect_desc_setup(&dev->vdev, qs->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); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -398,61 +384,51 @@ static void pci_indirect(void) g_free(data); g_free(indirect); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_config(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; int n_size = TEST_IMAGE_SIZE / 2; - void *addr; uint64_t capacity; - bus = pci_test_start(); + qs = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - /* MSI-X is not enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, n_size / 512); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + + qtest_shutdown(qs); } static void pci_msix(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; 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; @@ -460,41 +436,34 @@ static void pci_msix(void) uint8_t status; char *data; - bus = pci_test_start(); - alloc = pc_alloc_init(); + qs = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); qpci_msix_enable(dev->pdev); - qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); + qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); - /* MSI-X is enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true); - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -504,22 +473,22 @@ static void pci_msix(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &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, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -527,7 +496,7 @@ static void pci_msix(void) req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -535,10 +504,10 @@ static void pci_msix(void) 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); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); @@ -549,26 +518,22 @@ static void pci_msix(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_idx(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; QVirtioBlkReq req; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -576,33 +541,27 @@ static void pci_idx(void) uint8_t status; char *data; - bus = pci_test_start(); - alloc = pc_alloc_init(); + qs = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); qpci_msix_enable(dev->pdev); - qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); + qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); - /* MSI-X is enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true); - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -611,17 +570,16 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &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, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -630,7 +588,7 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -639,15 +597,15 @@ static void pci_idx(void) 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); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); /* No notification expected */ - status = qvirtio_wait_status_byte_no_isr(&qvirtio_pci, &dev->vdev, + status = qvirtio_wait_status_byte_no_isr(&dev->vdev, &vqpci->vq, req_addr + 528, QVIRTIO_BLK_TIMEOUT_US); g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -655,7 +613,7 @@ static void pci_idx(void) req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -663,9 +621,9 @@ static void pci_idx(void) 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); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); @@ -676,38 +634,38 @@ static void pci_idx(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_hotplug(void) { - QPCIBus *bus; QVirtioPCIDevice *dev; + QOSState *qs; + const char *arch = qtest_get_arch(); - bus = pci_test_start(); + qs = pci_test_start(); /* plug secondary disk */ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, "'drive': 'drive1'"); - dev = virtio_blk_pci_init(bus, PCI_SLOT_HP); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); g_assert(dev); qvirtio_pci_device_disable(dev); g_free(dev); /* unplug secondary disk */ - qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); - qpci_free_pc(bus); - test_end(); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + } + qtest_shutdown(qs); } static void mmio_basic(void) @@ -724,30 +682,27 @@ static void mmio_basic(void) g_assert(dev != NULL); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - qvirtio_reset(&qvirtio_mmio, &dev->vdev); - qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev); - qvirtio_set_driver(&qvirtio_mmio, &dev->vdev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); - vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0); + vq = qvirtqueue_setup(&dev->vdev, alloc, 0); - test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq, - QVIRTIO_MMIO_DEVICE_SPECIFIC); + test_basic(&dev->vdev, alloc, vq); qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev, - QVIRTIO_MMIO_DEVICE_SPECIFIC); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, n_size / 512); /* End test */ - qvirtqueue_cleanup(&qvirtio_mmio, vq, alloc); - generic_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); g_free(dev); + generic_alloc_uninit(alloc); test_end(); } @@ -757,12 +712,15 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || + strcmp(arch, "ppc64") == 0) { qtest_add_func("/virtio/blk/pci/basic", pci_basic); qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); qtest_add_func("/virtio/blk/pci/config", pci_config); - qtest_add_func("/virtio/blk/pci/msix", pci_msix); - qtest_add_func("/virtio/blk/pci/idx", pci_idx); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/virtio/blk/pci/msix", pci_msix); + qtest_add_func("/virtio/blk/pci/idx", pci_idx); + } qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); } else if (strcmp(arch, "arm") == 0) { qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index a343a6b048..8f94360480 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -12,12 +12,10 @@ #include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/iov.h" -#include "libqos/pci-pc.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" -#include "libqos/malloc-generic.h" #include "qemu/bswap.h" #include "hw/virtio/virtio-net.h" #include "standard-headers/linux/virtio_ids.h" @@ -46,39 +44,43 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); 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); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); return dev; } -static QPCIBus *pci_test_start(int socket) +static QOSState *pci_test_start(int socket) { - char *cmdline; - - cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 -device " - "virtio-net-pci,netdev=hs0", socket); - qtest_start(cmdline); - g_free(cmdline); - - return qpci_init_pc(NULL); + const char *arch = qtest_get_arch(); + const char *cmd = "-netdev socket,fd=%d,id=hs0 -device " + "virtio-net-pci,netdev=hs0"; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + return qtest_pc_boot(cmd, socket); + } + if (strcmp(arch, "ppc64") == 0) { + return qtest_spapr_boot(cmd, socket); + } + g_printerr("virtio-net tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } -static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev) +static void driver_init(QVirtioDevice *dev) { uint32_t features; - features = qvirtio_get_features(bus, dev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(bus, dev, features); + qvirtio_set_features(dev, features); - qvirtio_set_driver_ok(bus, dev); + qvirtio_set_driver_ok(dev); } -static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void rx_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) { @@ -101,19 +103,19 @@ static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev, req_addr = guest_alloc(alloc, 64); free_head = qvirtqueue_add(vq, req_addr, 64, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US); memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); g_assert_cmpstr(buffer, ==, "TEST"); guest_free(alloc, req_addr); } -static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void tx_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) { @@ -127,9 +129,9 @@ static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); free_head = qvirtqueue_add(vq, req_addr, 64, false, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US); guest_free(alloc, req_addr); ret = qemu_recv(socket, &len, sizeof(len), 0); @@ -140,7 +142,7 @@ static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, g_assert_cmpstr(buffer, ==, "TEST"); } -static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void rx_stop_cont_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) { @@ -164,7 +166,7 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, req_addr = guest_alloc(alloc, 64); free_head = qvirtqueue_add(vq, req_addr, 64, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); rsp = qmp("{ 'execute' : 'stop'}"); QDECREF(rsp); @@ -180,36 +182,34 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, rsp = qmp("{ 'execute' : 'cont'}"); QDECREF(rsp); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US); memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); g_assert_cmpstr(buffer, ==, "TEST"); guest_free(alloc, req_addr); } -static void send_recv_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void send_recv_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *rvq, QVirtQueue *tvq, int socket) { - rx_test(bus, dev, alloc, rvq, socket); - tx_test(bus, dev, alloc, tvq, socket); + rx_test(dev, alloc, rvq, socket); + tx_test(dev, alloc, tvq, socket); } -static void stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void stop_cont_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *rvq, QVirtQueue *tvq, int socket) { - rx_stop_cont_test(bus, dev, alloc, rvq, socket); + rx_stop_cont_test(dev, alloc, rvq, socket); } static void pci_basic(gconstpointer data) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *tx, *rx; - QGuestAllocator *alloc; - void (*func) (const QVirtioBus *bus, - QVirtioDevice *dev, + void (*func) (QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *rvq, QVirtQueue *tvq, @@ -219,37 +219,37 @@ static void pci_basic(gconstpointer data) ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); g_assert_cmpint(ret, !=, -1); - bus = pci_test_start(sv[1]); - dev = virtio_net_pci_init(bus, PCI_SLOT); + qs = pci_test_start(sv[1]); + dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT); - alloc = pc_alloc_init(); - rx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - tx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 1); + rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1); - driver_init(&qvirtio_pci, &dev->vdev); - func(&qvirtio_pci, &dev->vdev, alloc, &rx->vq, &tx->vq, sv[0]); + driver_init(&dev->vdev); + func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]); /* End test */ close(sv[0]); - qvirtqueue_cleanup(&qvirtio_pci, &tx->vq, alloc); - qvirtqueue_cleanup(&qvirtio_pci, &rx->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev->pdev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } #endif static void hotplug(void) { + const char *arch = qtest_get_arch(); + qtest_start("-device virtio-net-pci"); qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL); - qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); + } test_end(); } diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index e1b26401f9..dcecf77463 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -20,8 +20,13 @@ static void pci_nop(void) static void hotplug(void) { + const char *arch = qtest_get_arch(); + qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL); - qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); + } } int main(int argc, char **argv) diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 79088bb249..69220ef07b 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -11,12 +11,10 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "block/scsi.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.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 "libqos/malloc-generic.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" #include "standard-headers/linux/virtio_scsi.h" @@ -29,28 +27,32 @@ typedef struct { QVirtioDevice *dev; - QGuestAllocator *alloc; - QPCIBus *bus; + QOSState *qs; int num_queues; QVirtQueue *vq[MAX_NUM_QUEUES + 2]; } QVirtIOSCSI; -static void qvirtio_scsi_start(const char *extra_opts) +static QOSState *qvirtio_scsi_start(const char *extra_opts) { - char *cmdline; - - cmdline = g_strdup_printf( - "-drive id=drv0,if=none,file=/dev/null,format=raw " - "-device virtio-scsi-pci,id=vs0 " - "-device scsi-hd,bus=vs0.0,drive=drv0 %s", - extra_opts ? : ""); - qtest_start(cmdline); - g_free(cmdline); + const char *arch = qtest_get_arch(); + const char *cmd = "-drive id=drv0,if=none,file=/dev/null,format=raw " + "-device virtio-scsi-pci,id=vs0 " + "-device scsi-hd,bus=vs0.0,drive=drv0 %s"; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + return qtest_pc_boot(cmd, extra_opts ? : ""); + } + if (strcmp(arch, "ppc64") == 0) { + return qtest_spapr_boot(cmd, extra_opts ? : ""); + } + + g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } -static void qvirtio_scsi_stop(void) +static void qvirtio_scsi_stop(QOSState *qs) { - qtest_end(); + qtest_shutdown(qs); } static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) @@ -58,12 +60,12 @@ static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) int i; for (i = 0; i < vs->num_queues + 2; i++) { - qvirtqueue_cleanup(&qvirtio_pci, vs->vq[i], vs->alloc); + qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], vs->qs->alloc); } - pc_alloc_uninit(vs->alloc); qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev)); g_free(vs->dev); - qpci_free_pc(vs->bus); + qvirtio_scsi_stop(vs->qs); + g_free(vs); } static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, @@ -71,7 +73,7 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, { uint64_t addr; - addr = guest_alloc(vs->alloc, alloc_size); + addr = guest_alloc(vs->qs->alloc, alloc_size); if (data) { memwrite(addr, data, alloc_size); } @@ -118,8 +120,8 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, qvirtqueue_add(vq, data_in_addr, data_in_len, true, false); } - qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US); + qvirtqueue_kick(vs->dev, vq, free_head); + qvirtio_wait_queue_isr(vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US); response = readb(resp_addr + offsetof(struct virtio_scsi_cmd_resp, response)); @@ -128,10 +130,10 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, memread(resp_addr, resp_out, sizeof(*resp_out)); } - guest_free(vs->alloc, req_addr); - guest_free(vs->alloc, resp_addr); - guest_free(vs->alloc, data_in_addr); - guest_free(vs->alloc, data_out_addr); + guest_free(vs->qs->alloc, req_addr); + guest_free(vs->qs->alloc, resp_addr); + guest_free(vs->qs->alloc, data_in_addr); + guest_free(vs->qs->alloc, data_out_addr); return response; } @@ -141,31 +143,29 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) QVirtIOSCSI *vs; QVirtioPCIDevice *dev; struct virtio_scsi_cmd_resp resp; - void *addr; int i; vs = g_new0(QVirtIOSCSI, 1); - vs->alloc = pc_alloc_init(); - vs->bus = qpci_init_pc(NULL); - dev = qvirtio_pci_device_find(vs->bus, VIRTIO_ID_SCSI); + vs->qs = qvirtio_scsi_start("-drive file=blkdebug::null-co://," + "if=none,id=dr1,format=raw,file.align=4k " + "-device scsi-disk,drive=dr1,lun=0,scsi-id=1"); + dev = qvirtio_pci_device_find(vs->qs->pcibus, VIRTIO_ID_SCSI); vs->dev = (QVirtioDevice *)dev; g_assert(dev != NULL); g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI); qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, vs->dev); - qvirtio_set_acknowledge(&qvirtio_pci, vs->dev); - qvirtio_set_driver(&qvirtio_pci, vs->dev); + qvirtio_reset(vs->dev); + qvirtio_set_acknowledge(vs->dev); + qvirtio_set_driver(vs->dev); - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); - vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev, - (uint64_t)(uintptr_t)addr); + vs->num_queues = qvirtio_config_readl(vs->dev, 0); g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i); + vs->vq[i] = qvirtqueue_setup(vs->dev, vs->qs->alloc, i); } /* Clear the POWER ON OCCURRED unit attention */ @@ -184,15 +184,18 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { - qvirtio_scsi_start(NULL); - qvirtio_scsi_stop(); + QOSState *qs; + + qs = qvirtio_scsi_start(NULL); + qvirtio_scsi_stop(qs); } static void hotplug(void) { QDict *response; + QOSState *qs; - qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw"); + qs = qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw"); response = qmp("{\"execute\": \"device_add\"," " \"arguments\": {" " \"driver\": \"scsi-hd\"," @@ -214,7 +217,7 @@ static void hotplug(void) g_assert(qdict_haskey(response, "event")); g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); QDECREF(response); - qvirtio_scsi_stop(); + qvirtio_scsi_stop(qs); } /* Test WRITE SAME with the lba not aligned */ @@ -230,9 +233,6 @@ static void test_unaligned_write_same(void) 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; - qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1" - ",format=raw,file.align=4k " - "-device scsi-disk,drive=dr1,lun=0,scsi-id=1"); vs = qvirtio_scsi_pci_init(PCI_SLOT); g_assert_cmphex(0, ==, @@ -242,7 +242,6 @@ static void test_unaligned_write_same(void) virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); qvirtio_scsi_pci_free(vs); - qvirtio_scsi_stop(); } int main(int argc, char **argv) diff --git a/ui/curses.c b/ui/curses.c index d06f724879..2e132a7bfa 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -369,10 +369,10 @@ static void curses_setup(void) /* ACS_* is not constant. So, we can't initialize statically. */ vga_to_curses['\0'] = ' '; vga_to_curses[0x04] = ACS_DIAMOND; - vga_to_curses[0x0a] = ACS_RARROW; - vga_to_curses[0x0b] = ACS_LARROW; vga_to_curses[0x18] = ACS_UARROW; vga_to_curses[0x19] = ACS_DARROW; + vga_to_curses[0x1a] = ACS_RARROW; + vga_to_curses[0x1b] = ACS_LARROW; vga_to_curses[0x9c] = ACS_STERLING; vga_to_curses[0xb0] = ACS_BOARD; vga_to_curses[0xb1] = ACS_CKBOARD; @@ -912,9 +912,28 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, if (!qemu_input_is_absolute() && s->ptr_owner == vc) { GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); + int screen_width, screen_height; + int x = (int)motion->x_root; int y = (int)motion->y_root; +#if GTK_CHECK_VERSION(3, 22, 0) + { + GdkDisplay *dpy = gtk_widget_get_display(widget); + GdkWindow *win = gtk_widget_get_window(widget); + GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); + GdkRectangle geometry; + gdk_monitor_get_geometry(monitor, &geometry); + screen_width = geometry.width; + screen_height = geometry.height; + } +#else + { + screen_width = gdk_screen_get_width(screen); + screen_height = gdk_screen_get_height(screen); + } +#endif + /* In relative mode check to see if client pointer hit * one of the screen edges, and if so move it back by * 200 pixels. This is important because the pointer @@ -928,10 +947,10 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, if (y == 0) { y += 200; } - if (x == (gdk_screen_get_width(screen) - 1)) { + if (x == (screen_width - 1)) { x -= 200; } - if (y == (gdk_screen_get_height(screen) - 1)) { + if (y == (screen_height - 1)) { y -= 200; } @@ -1051,7 +1070,9 @@ static gboolean gd_text_key_down(GtkWidget *widget, VirtualConsole *vc = opaque; QemuConsole *con = vc->gfx.dcl.con; - if (key->length) { + if (key->keyval == GDK_KEY_Delete) { + kbd_put_qcode_console(con, Q_KEY_CODE_DELETE); + } else if (key->length) { kbd_put_string_console(con, key->string, key->length); } else { int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget), diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index 11c8c9a819..c0c29a5119 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -116,6 +116,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len return 0; } + qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls"); VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(tls); diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 42a8e7be5c..bffb484a8d 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -67,6 +67,8 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, return TRUE; } + qio_channel_set_name(QIO_CHANNEL(tls), "vnc-ws-server-tls"); + VNC_DEBUG("Start TLS WS handshake process\n"); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(tls); @@ -113,6 +115,7 @@ gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, } wioc = qio_channel_websock_new_server(vs->ioc); + qio_channel_set_name(QIO_CHANNEL(wioc), "vnc-ws-server-websock"); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(wioc); @@ -3100,6 +3100,9 @@ static gboolean vnc_listen_io(QIOChannel *ioc, sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err); if (sioc != NULL) { + qio_channel_set_name(QIO_CHANNEL(sioc), + ioc != QIO_CHANNEL(vd->lsock) ? + "vnc-ws-server" : "vnc-server"); qio_channel_set_delay(QIO_CHANNEL(sioc), false); vnc_connect(vd, sioc, false, ioc != QIO_CHANNEL(vd->lsock)); @@ -3788,6 +3791,7 @@ void vnc_display_open(const char *id, Error **errp) } vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; sioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) { goto fail; } @@ -3795,6 +3799,7 @@ void vnc_display_open(const char *id, Error **errp) object_unref(OBJECT(sioc)); } else { vd->lsock = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(vd->lsock), "vnc-listen"); if (qio_channel_socket_listen_sync(vd->lsock, saddr, errp) < 0) { goto fail; } @@ -3802,6 +3807,7 @@ void vnc_display_open(const char *id, Error **errp) if (ws_enabled) { vd->lwebsock = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(vd->lwebsock), "vnc-ws-listen"); if (qio_channel_socket_listen_sync(vd->lwebsock, wsaddr, errp) < 0) { object_unref(OBJECT(vd->lsock)); @@ -3845,6 +3851,7 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth) sioc = qio_channel_socket_new_fd(csock, NULL); if (sioc) { + qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-server"); vnc_connect(vd, sioc, skipauth, false); object_unref(OBJECT(sioc)); } diff --git a/util/Makefile.objs b/util/Makefile.objs index 36c7dcc1fa..ad0f9c7fe4 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -25,7 +25,6 @@ util-obj-y += uuid.o util-obj-y += throttle.o util-obj-y += getauxval.o util-obj-y += readline.o -util-obj-y += rfifolock.o util-obj-y += rcu.o util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o util-obj-y += qemu-coroutine-sleep.o diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 8ec99ccb4f..67c65893a4 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include <termios.h> -#include <termios.h> #include <glib/gprintf.h> diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index ce51b37c1d..d20cddec0c 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -80,6 +80,20 @@ void qemu_mutex_unlock(QemuMutex *mutex) error_exit(err, __func__); } +void qemu_rec_mutex_init(QemuRecMutex *mutex) +{ + int err; + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + err = pthread_mutex_init(&mutex->lock, &attr); + pthread_mutexattr_destroy(&attr); + if (err) { + error_exit(err, __func__); + } +} + void qemu_cond_init(QemuCond *cond) { int err; diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 072806f792..728e76b5b2 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -79,6 +79,31 @@ void qemu_mutex_unlock(QemuMutex *mutex) LeaveCriticalSection(&mutex->lock); } +void qemu_rec_mutex_init(QemuRecMutex *mutex) +{ + InitializeCriticalSection(&mutex->lock); +} + +void qemu_rec_mutex_destroy(QemuRecMutex *mutex) +{ + DeleteCriticalSection(&mutex->lock); +} + +void qemu_rec_mutex_lock(QemuRecMutex *mutex) +{ + EnterCriticalSection(&mutex->lock); +} + +int qemu_rec_mutex_trylock(QemuRecMutex *mutex) +{ + return !TryEnterCriticalSection(&mutex->lock); +} + +void qemu_rec_mutex_unlock(QemuRecMutex *mutex) +{ + LeaveCriticalSection(&mutex->lock); +} + void qemu_cond_init(QemuCond *cond) { memset(cond, 0, sizeof(*cond)); diff --git a/util/rfifolock.c b/util/rfifolock.c deleted file mode 100644 index 084c2f0ea1..0000000000 --- a/util/rfifolock.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Recursive FIFO lock - * - * Copyright Red Hat, Inc. 2013 - * - * Authors: - * Stefan Hajnoczi <stefanha@redhat.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/rfifolock.h" - -void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque) -{ - qemu_mutex_init(&r->lock); - r->head = 0; - r->tail = 0; - qemu_cond_init(&r->cond); - r->nesting = 0; - r->cb = cb; - r->cb_opaque = opaque; -} - -void rfifolock_destroy(RFifoLock *r) -{ - qemu_cond_destroy(&r->cond); - qemu_mutex_destroy(&r->lock); -} - -/* - * Theory of operation: - * - * In order to ensure FIFO ordering, implement a ticketlock. Threads acquiring - * the lock enqueue themselves by incrementing the tail index. When the lock - * is unlocked, the head is incremented and waiting threads are notified. - * - * Recursive locking does not take a ticket since the head is only incremented - * when the outermost recursive caller unlocks. - */ -void rfifolock_lock(RFifoLock *r) -{ - qemu_mutex_lock(&r->lock); - - /* Take a ticket */ - unsigned int ticket = r->tail++; - - if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) { - r->tail--; /* put ticket back, we're nesting */ - } else { - while (ticket != r->head) { - /* Invoke optional contention callback */ - if (r->cb) { - r->cb(r->cb_opaque); - } - qemu_cond_wait(&r->cond, &r->lock); - } - qemu_thread_get_self(&r->owner_thread); - } - - r->nesting++; - qemu_mutex_unlock(&r->lock); -} - -void rfifolock_unlock(RFifoLock *r) -{ - qemu_mutex_lock(&r->lock); - assert(r->nesting > 0); - assert(qemu_thread_is_self(&r->owner_thread)); - if (--r->nesting == 0) { - r->head++; - qemu_cond_broadcast(&r->cond); - } - qemu_mutex_unlock(&r->lock); -} @@ -90,6 +90,7 @@ int main(int argc, char **argv) #include "audio/audio.h" #include "migration/migration.h" #include "sysemu/cpus.h" +#include "migration/colo.h" #include "sysemu/kvm.h" #include "qapi/qmp/qjson.h" #include "qemu/option.h" @@ -110,7 +111,6 @@ int main(int argc, char **argv) #include "trace.h" #include "trace/control.h" #include "qemu/queue.h" -#include "sysemu/cpus.h" #include "sysemu/arch_init.h" #include "ui/qemu-spice.h" @@ -218,6 +218,7 @@ static struct { { .driver = "isa-serial", .flag = &default_serial }, { .driver = "isa-parallel", .flag = &default_parallel }, { .driver = "isa-fdc", .flag = &default_floppy }, + { .driver = "floppy", .flag = &default_floppy }, { .driver = "ide-cd", .flag = &default_cdrom }, { .driver = "ide-hd", .flag = &default_cdrom }, { .driver = "ide-drive", .flag = &default_cdrom }, @@ -612,6 +613,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_INMIGRATE, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_INMIGRATE, RUN_STATE_PRELAUNCH }, { RUN_STATE_INMIGRATE, RUN_STATE_POSTMIGRATE }, + { RUN_STATE_INMIGRATE, RUN_STATE_COLO }, { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PAUSED }, { RUN_STATE_INTERNAL_ERROR, RUN_STATE_FINISH_MIGRATE }, @@ -624,6 +626,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PAUSED, RUN_STATE_RUNNING }, { RUN_STATE_PAUSED, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_PAUSED, RUN_STATE_PRELAUNCH }, + { RUN_STATE_PAUSED, RUN_STATE_COLO}, { RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE }, @@ -636,10 +639,13 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_POSTMIGRATE }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PRELAUNCH }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_COLO}, { RUN_STATE_RESTORE_VM, RUN_STATE_RUNNING }, { RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH }, + { RUN_STATE_COLO, RUN_STATE_RUNNING }, + { RUN_STATE_RUNNING, RUN_STATE_DEBUG }, { RUN_STATE_RUNNING, RUN_STATE_INTERNAL_ERROR }, { RUN_STATE_RUNNING, RUN_STATE_IO_ERROR }, @@ -650,6 +656,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_RUNNING, RUN_STATE_SHUTDOWN }, { RUN_STATE_RUNNING, RUN_STATE_WATCHDOG }, { RUN_STATE_RUNNING, RUN_STATE_GUEST_PANICKED }, + { RUN_STATE_RUNNING, RUN_STATE_COLO}, { RUN_STATE_SAVE_VM, RUN_STATE_RUNNING }, @@ -662,10 +669,12 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_SUSPENDED, RUN_STATE_RUNNING }, { RUN_STATE_SUSPENDED, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_SUSPENDED, RUN_STATE_PRELAUNCH }, + { RUN_STATE_SUSPENDED, RUN_STATE_COLO}, { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING }, { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_WATCHDOG, RUN_STATE_PRELAUNCH }, + { RUN_STATE_WATCHDOG, RUN_STATE_COLO}, { RUN_STATE_GUEST_PANICKED, RUN_STATE_RUNNING }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE }, @@ -2407,8 +2416,9 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) if (qemu_opt_get_bool(opts, "pretty", 0)) flags |= MONITOR_USE_PRETTY; - if (qemu_opt_get_bool(opts, "default", 0)) - flags |= MONITOR_IS_DEFAULT; + if (qemu_opt_get_bool(opts, "default", 0)) { + error_report("option 'default' does nothing and is deprecated"); + } chardev = qemu_opt_get(opts, "chardev"); chr = qemu_chr_find(chardev); @@ -2427,16 +2437,12 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty) QemuOpts *opts; const char *p; char label[32]; - int def = 0; if (strstart(optarg, "chardev:", &p)) { snprintf(label, sizeof(label), "%s", p); } else { snprintf(label, sizeof(label), "compat_monitor%d", monitor_device_index); - if (monitor_device_index == 0) { - def = 1; - } opts = qemu_chr_parse_compat(label, optarg); if (!opts) { error_report("parse error: %s", optarg); @@ -2448,8 +2454,6 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty) qemu_opt_set(opts, "mode", mode, &error_abort); qemu_opt_set(opts, "chardev", label, &error_abort); qemu_opt_set_bool(opts, "pretty", pretty, &error_abort); - if (def) - qemu_opt_set(opts, "default", "on", &error_abort); monitor_device_index++; } @@ -4425,6 +4429,8 @@ int main(int argc, char **argv, char **envp) #endif } + colo_info_init(); + if (net_init_clients() < 0) { exit(1); } diff --git a/xen-common.c b/xen-common.c index e641ad1aef..909976071c 100644 --- a/xen-common.c +++ b/xen-common.c @@ -116,12 +116,12 @@ static int xen_init(MachineState *ms) { xen_xc = xc_interface_open(0, 0, 0); if (xen_xc == NULL) { - xen_be_printf(NULL, 0, "can't open xen interface\n"); + xen_pv_printf(NULL, 0, "can't open xen interface\n"); return -1; } xen_fmem = xenforeignmemory_open(0, 0); if (xen_fmem == NULL) { - xen_be_printf(NULL, 0, "can't open xen fmem interface\n"); + xen_pv_printf(NULL, 0, "can't open xen fmem interface\n"); xc_interface_close(xen_xc); return -1; } |