From 08801d190afd21f7d3db9a2cdce2b1528903ac2c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 8 Feb 2022 15:10:25 +0800 Subject: [PATCH 001/486] net/dump.c: Suppress spurious compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling with gcc version 11.2.0 (Ubuntu 11.2.0-13ubuntu1) results in a (spurious) warning: In function ‘dump_receive_iov’, inlined from ‘filter_dump_receive_iov’ at ../net/dump.c:157:5: ../net/dump.c:89:9: error: ‘writev’ specified size 18446744073709551600 exceeds maximum object size 9223372036854775807 [-Werror=stringop-overflow=] 89 | if (writev(s->fd, dumpiov, cnt + 1) != sizeof(hdr) + caplen) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /home/ptomsich/qemu/include/qemu/osdep.h:108, from ../net/dump.c:25: ../net/dump.c: In function ‘filter_dump_receive_iov’: /usr/include/x86_64-linux-gnu/sys/uio.h:52:16: note: in a call to function ‘writev’ declared with attribute ‘read_only (2, 3)’ 52 | extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count) | ^~~~~~ cc1: all warnings being treated as errors This change helps that version of GCC to understand what is going on and suppresses this warning. Signed-off-by: Philipp Tomsich --- net/dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/dump.c b/net/dump.c index a07ba62401..c32d3bf4e6 100644 --- a/net/dump.c +++ b/net/dump.c @@ -86,7 +86,7 @@ static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt) dumpiov[0].iov_len = sizeof(hdr); cnt = iov_copy(&dumpiov[1], cnt, iov, cnt, 0, caplen); - if (writev(s->fd, dumpiov, cnt + 1) != sizeof(hdr) + caplen) { + if (writev(s->fd, &dumpiov[0], cnt + 1) != sizeof(hdr) + caplen) { error_report("network dump write error - stopping dump"); close(s->fd); s->fd = -1; -- Gitee From 4c3d47e04886e072acc0e4fefdc49e9d1f6b4ad1 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:45 +0800 Subject: [PATCH 002/486] qapi/block-core: Add retry option for error action Add a new error action 'retry' to support retry on errors. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- blockdev.c | 2 ++ qapi/block-core.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index b35072644e..6f1981635b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -333,6 +333,8 @@ static int parse_block_error_action(const char *buf, bool is_read, Error **errp) return BLOCKDEV_ON_ERROR_STOP; } else if (!strcmp(buf, "report")) { return BLOCKDEV_ON_ERROR_REPORT; + } else if (!strcmp(buf, "retry")) { + return BLOCKDEV_ON_ERROR_RETRY; } else { error_setg(errp, "'%s' invalid %s error action", buf, is_read ? "read" : "write"); diff --git a/qapi/block-core.json b/qapi/block-core.json index 1d3dd9cb48..804beabfb0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1146,7 +1146,7 @@ # Since: 1.3 ## { 'enum': 'BlockdevOnError', - 'data': ['report', 'ignore', 'enospc', 'stop', 'auto'] } + 'data': ['report', 'ignore', 'enospc', 'stop', 'auto', 'retry'] } ## # @MirrorSyncMode: @@ -4952,7 +4952,7 @@ # Since: 2.1 ## { 'enum': 'BlockErrorAction', - 'data': [ 'ignore', 'report', 'stop' ] } + 'data': [ 'ignore', 'report', 'stop', 'retry' ] } ## -- Gitee From 4dc180e87fb641f64fce7be3a0807488d0cc0a51 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:46 +0800 Subject: [PATCH 003/486] block-backend: Introduce retry timer Add a timer to regularly trigger retry on errors. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- block/block-backend.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 12ef80ea17..257cd775c0 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -35,6 +35,9 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); +/* block backend default retry interval */ +#define BLOCK_BACKEND_DEFAULT_RETRY_INTERVAL 1000 + typedef struct BlockBackendAioNotifier { void (*attached_aio_context)(AioContext *new_context, void *opaque); void (*detach_aio_context)(void *opaque); @@ -95,6 +98,15 @@ struct BlockBackend { * Accessed with atomic ops. */ unsigned int in_flight; + + /* Timer for retry on errors. */ + QEMUTimer *retry_timer; + /* Interval in ms to trigger next retry. */ + int64_t retry_interval; + /* Start time of the first error. Used to check timeout. */ + int64_t retry_start_time; + /* Retry timeout. 0 represents infinite retry. */ + int64_t retry_timeout; }; typedef struct BlockBackendAIOCB { @@ -353,6 +365,11 @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; + blk->retry_timer = NULL; + blk->retry_interval = BLOCK_BACKEND_DEFAULT_RETRY_INTERVAL; + blk->retry_start_time = 0; + blk->retry_timeout = 0; + block_acct_init(&blk->stats); qemu_co_queue_init(&blk->queued_requests); @@ -471,6 +488,10 @@ static void blk_delete(BlockBackend *blk) QTAILQ_REMOVE(&block_backends, blk, link); drive_info_del(blk->legacy_dinfo); block_acct_cleanup(&blk->stats); + if (blk->retry_timer) { + timer_del(blk->retry_timer); + timer_free(blk->retry_timer); + } g_free(blk); } -- Gitee From dfda8c57de71f2f10b57cf21b1e36f18d4ed37a3 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:47 +0800 Subject: [PATCH 004/486] block-backend: Add device specific retry callback Add retry_request_cb in BlockDevOps to do device specific retry action. Backend's timer would be registered only when the backend is set 'retry' on errors and the device supports retry action. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- block/block-backend.c | 8 ++++++++ include/sysemu/block-backend.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 257cd775c0..24003adf0b 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1018,6 +1018,14 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, blk->dev_ops = ops; blk->dev_opaque = opaque; + if ((blk->on_read_error == BLOCKDEV_ON_ERROR_RETRY || + blk->on_write_error == BLOCKDEV_ON_ERROR_RETRY) && + ops->retry_request_cb) { + blk->retry_timer = aio_timer_new(blk->ctx, QEMU_CLOCK_REALTIME, + SCALE_MS, ops->retry_request_cb, + opaque); + } + /* Are we currently quiesced? Should we enforce this right now? */ if (blk->quiesce_counter && ops->drained_begin) { ops->drained_begin(opaque); diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index e5e1524f06..a7a13d47de 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -70,6 +70,10 @@ typedef struct BlockDevOps { * Is the device still busy? */ bool (*drained_poll)(void *opaque); + /* + * Runs when retrying failed requests. + */ + void (*retry_request_cb)(void *opaque); } BlockDevOps; /* This struct is embedded in (the private) BlockBackend struct and contains -- Gitee From 2e1c75e5a0339d2bf417e5a4437d8e627a303286 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:48 +0800 Subject: [PATCH 005/486] block-backend: Enable retry action on errors Enable retry action when backend's retry timer is available. It would trigger the timer to do device specific retry action. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- block/block-backend.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 24003adf0b..5a016d32fa 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1793,6 +1793,9 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, return BLOCK_ERROR_ACTION_REPORT; case BLOCKDEV_ON_ERROR_IGNORE: return BLOCK_ERROR_ACTION_IGNORE; + case BLOCKDEV_ON_ERROR_RETRY: + return (blk->retry_timer) ? + BLOCK_ERROR_ACTION_RETRY : BLOCK_ERROR_ACTION_REPORT; case BLOCKDEV_ON_ERROR_AUTO: default: abort(); @@ -1840,6 +1843,10 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, qemu_system_vmstop_request_prepare(); send_qmp_error_event(blk, action, is_read, error); qemu_system_vmstop_request(RUN_STATE_IO_ERROR); + } else if (action == BLOCK_ERROR_ACTION_RETRY) { + timer_mod(blk->retry_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + + blk->retry_interval); + send_qmp_error_event(blk, action, is_read, error); } else { send_qmp_error_event(blk, action, is_read, error); } -- Gitee From 953590f4854d75e6051237f668c9fb393235f471 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:49 +0800 Subject: [PATCH 006/486] block-backend: Add timeout support for retry Retry should only be triggered when timeout is not reached, so let's check timeout before retry. Device should also reset retry_start_time after successful retry. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- block/block-backend.c | 25 ++++++++++++++++++++++++- include/sysemu/block-backend.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/block/block-backend.c b/block/block-backend.c index 5a016d32fa..37e21c473e 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1766,6 +1766,29 @@ void blk_drain_all(void) bdrv_drain_all_end(); } +static bool blk_error_retry_timeout(BlockBackend *blk) +{ + /* No timeout set, infinite retries. */ + if (!blk->retry_timeout) { + return false; + } + + /* The first time an error occurs. */ + if (!blk->retry_start_time) { + blk->retry_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + return false; + } + + return qemu_clock_get_ms(QEMU_CLOCK_REALTIME) > (blk->retry_start_time + + blk->retry_timeout); +} + +void blk_error_retry_reset_timeout(BlockBackend *blk) +{ + if (blk->retry_timer && blk->retry_start_time) + blk->retry_start_time = 0; +} + void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, BlockdevOnError on_write_error) { @@ -1794,7 +1817,7 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, case BLOCKDEV_ON_ERROR_IGNORE: return BLOCK_ERROR_ACTION_IGNORE; case BLOCKDEV_ON_ERROR_RETRY: - return (blk->retry_timer) ? + return (blk->retry_timer && !blk_error_retry_timeout(blk)) ? BLOCK_ERROR_ACTION_RETRY : BLOCK_ERROR_ACTION_REPORT; case BLOCKDEV_ON_ERROR_AUTO: default: diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index a7a13d47de..56a403883d 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -198,6 +198,7 @@ void blk_inc_in_flight(BlockBackend *blk); void blk_dec_in_flight(BlockBackend *blk); void blk_drain(BlockBackend *blk); void blk_drain_all(void); +void blk_error_retry_reset_timeout(BlockBackend *blk); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, BlockdevOnError on_write_error); BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); -- Gitee From a58fda7b158441c645e143bf658d12914ffbc7b8 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:50 +0800 Subject: [PATCH 007/486] block: Add error retry param setting Add "retry_interval" and "retry_timeout" parameter for drive and device option. These parameter are valid only when werror/rerror=retry. eg. --drive file=image,rerror=retry,retry_interval=1000,retry_timeout=5000 Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- block/block-backend.c | 13 +++++++-- blockdev.c | 50 ++++++++++++++++++++++++++++++++++ hw/block/block.c | 10 +++++++ include/hw/block/block.h | 7 ++++- include/sysemu/block-backend.h | 5 ++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 37e21c473e..d3d90a95a5 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -35,9 +35,6 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); -/* block backend default retry interval */ -#define BLOCK_BACKEND_DEFAULT_RETRY_INTERVAL 1000 - typedef struct BlockBackendAioNotifier { void (*attached_aio_context)(AioContext *new_context, void *opaque); void (*detach_aio_context)(void *opaque); @@ -1766,6 +1763,16 @@ void blk_drain_all(void) bdrv_drain_all_end(); } +void blk_set_on_error_retry_interval(BlockBackend *blk, int64_t interval) +{ + blk->retry_interval = interval; +} + +void blk_set_on_error_retry_timeout(BlockBackend *blk, int64_t timeout) +{ + blk->retry_timeout = timeout; +} + static bool blk_error_retry_timeout(BlockBackend *blk) { /* No timeout set, infinite retries. */ diff --git a/blockdev.c b/blockdev.c index 6f1981635b..10a73fa423 100644 --- a/blockdev.c +++ b/blockdev.c @@ -480,6 +480,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, const char *buf; int bdrv_flags = 0; int on_read_error, on_write_error; + int64_t retry_interval, retry_timeout; bool account_invalid, account_failed; bool writethrough, read_only; BlockBackend *blk; @@ -572,6 +573,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, } } + retry_interval = qemu_opt_get_number(opts, "retry_interval", + BLOCK_BACKEND_DEFAULT_RETRY_INTERVAL); + retry_timeout = qemu_opt_get_number(opts, "retry_timeout", 0); + if (snapshot) { bdrv_flags |= BDRV_O_SNAPSHOT; } @@ -635,6 +640,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, blk_set_enable_write_cache(blk, !writethrough); blk_set_on_error(blk, on_read_error, on_write_error); + if (on_read_error == BLOCKDEV_ON_ERROR_RETRY || + on_write_error == BLOCKDEV_ON_ERROR_RETRY) { + blk_set_on_error_retry_interval(blk, retry_interval); + blk_set_on_error_retry_timeout(blk, retry_timeout); + } if (!monitor_add_blk(blk, id, errp)) { blk_unref(blk); @@ -761,6 +771,14 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "werror", .type = QEMU_OPT_STRING, .help = "write error action", + },{ + .name = "retry_interval", + .type = QEMU_OPT_NUMBER, + .help = "interval for retry action in millisecond", + },{ + .name = "retry_timeout", + .type = QEMU_OPT_NUMBER, + .help = "timeout for retry action in millisecond", },{ .name = "copy-on-read", .type = QEMU_OPT_BOOL, @@ -783,6 +801,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, BlockInterfaceType type; int max_devs, bus_id, unit_id, index; const char *werror, *rerror; + int64_t retry_interval, retry_timeout; bool read_only = false; bool copy_on_read; const char *filename; @@ -990,6 +1009,29 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, qdict_put_str(bs_opts, "rerror", rerror); } + if (qemu_opt_find(legacy_opts, "retry_interval")) { + if ((werror == NULL || strcmp(werror, "retry")) && + (rerror == NULL || strcmp(rerror, "retry"))) { + error_setg(errp, "retry_interval is only supported " + "by werror/rerror=retry"); + goto fail; + } + retry_interval = qemu_opt_get_number(legacy_opts, "retry_interval", + BLOCK_BACKEND_DEFAULT_RETRY_INTERVAL); + qdict_put_int(bs_opts, "retry_interval", retry_interval); + } + + if (qemu_opt_find(legacy_opts, "retry_timeout")) { + if ((werror == NULL || strcmp(werror, "retry")) && + (rerror == NULL || strcmp(rerror, "retry"))) { + error_setg(errp, "retry_timeout is only supported " + "by werror/rerror=retry"); + goto fail; + } + retry_timeout = qemu_opt_get_number(legacy_opts, "retry_timeout", 0); + qdict_put_int(bs_opts, "retry_timeout", retry_timeout); + } + /* Actual block device init: Functionality shared with blockdev-add */ blk = blockdev_init(filename, bs_opts, errp); bs_opts = NULL; @@ -3806,6 +3848,14 @@ QemuOptsList qemu_common_drive_opts = { .name = "werror", .type = QEMU_OPT_STRING, .help = "write error action", + },{ + .name = "retry_interval", + .type = QEMU_OPT_NUMBER, + .help = "interval for retry action in millisecond", + },{ + .name = "retry_timeout", + .type = QEMU_OPT_NUMBER, + .help = "timeout for retry action in millisecond", },{ .name = BDRV_OPT_READ_ONLY, .type = QEMU_OPT_BOOL, diff --git a/hw/block/block.c b/hw/block/block.c index d47ebf005a..26c0767552 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -206,6 +206,16 @@ bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, blk_set_enable_write_cache(blk, wce); blk_set_on_error(blk, rerror, werror); + if (rerror == BLOCKDEV_ON_ERROR_RETRY || + werror == BLOCKDEV_ON_ERROR_RETRY) { + if (conf->retry_interval >= 0) { + blk_set_on_error_retry_interval(blk, conf->retry_interval); + } + if (conf->retry_timeout >= 0) { + blk_set_on_error_retry_timeout(blk, conf->retry_timeout); + } + } + return true; } diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 5902c0440a..24fb7d77af 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -33,6 +33,8 @@ typedef struct BlockConf { bool share_rw; BlockdevOnError rerror; BlockdevOnError werror; + int64_t retry_interval; + int64_t retry_timeout; } BlockConf; static inline unsigned int get_physical_block_exp(BlockConf *conf) @@ -79,7 +81,10 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) DEFINE_PROP_BLOCKDEV_ON_ERROR("rerror", _state, _conf.rerror, \ BLOCKDEV_ON_ERROR_AUTO), \ DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \ - BLOCKDEV_ON_ERROR_AUTO) + BLOCKDEV_ON_ERROR_AUTO), \ + DEFINE_PROP_INT64("retry_interval", _state, _conf.retry_interval, \ + -1), \ + DEFINE_PROP_INT64("retry_timeout", _state, _conf.retry_timeout, -1) /* Backend access helpers */ diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 56a403883d..887c19ff5d 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -25,6 +25,9 @@ */ #include "block/block.h" +/* block backend default retry interval */ +#define BLOCK_BACKEND_DEFAULT_RETRY_INTERVAL 1000 + /* Callbacks for block device models */ typedef struct BlockDevOps { /* @@ -198,6 +201,8 @@ void blk_inc_in_flight(BlockBackend *blk); void blk_dec_in_flight(BlockBackend *blk); void blk_drain(BlockBackend *blk); void blk_drain_all(void); +void blk_set_on_error_retry_interval(BlockBackend *blk, int64_t interval); +void blk_set_on_error_retry_timeout(BlockBackend *blk, int64_t timeout); void blk_error_retry_reset_timeout(BlockBackend *blk); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, BlockdevOnError on_write_error); -- Gitee From a81122e37595fe1cc9eaa2adbbfccbfdf8f988b8 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:53 +0800 Subject: [PATCH 008/486] virtio_blk: Add support for retry on errors Insert failed requests into device's list for later retry and handle queued requests to implement retry_request_cb. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- hw/block/virtio-blk.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index f139cd7cc9..c8d94a3dfb 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -108,6 +108,10 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, block_acct_failed(blk_get_stats(s->blk), &req->acct); } virtio_blk_free_request(req); + } else if (action == BLOCK_ERROR_ACTION_RETRY) { + req->mr_next = NULL; + req->next = s->rq; + s->rq = req; } blk_error_action(s->blk, action, is_read, error); @@ -149,6 +153,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret) } } + blk_error_retry_reset_timeout(s->blk); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(s->blk), &req->acct); virtio_blk_free_request(req); @@ -168,6 +173,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret) } } + blk_error_retry_reset_timeout(s->blk); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(s->blk), &req->acct); virtio_blk_free_request(req); @@ -190,6 +196,7 @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) } } + blk_error_retry_reset_timeout(s->blk); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); if (is_write_zeroes) { block_acct_done(blk_get_stats(s->blk), &req->acct); @@ -828,12 +835,12 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh) { - VirtIOBlockReq *req = s->rq; + VirtIOBlockReq *req; MultiReqBuffer mrb = {}; - s->rq = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); + req = s->rq; + s->rq = NULL; while (req) { VirtIOBlockReq *next = req->next; if (virtio_blk_handle_request(req, &mrb)) { @@ -1138,8 +1145,16 @@ static void virtio_blk_resize(void *opaque) aio_bh_schedule_oneshot(qemu_get_aio_context(), virtio_resize_cb, vdev); } +static void virtio_blk_retry_request(void *opaque) +{ + VirtIOBlock *s = VIRTIO_BLK(opaque); + + virtio_blk_process_queued_requests(s, false); +} + static const BlockDevOps virtio_block_ops = { .resize_cb = virtio_blk_resize, + .retry_request_cb = virtio_blk_retry_request, }; static void virtio_blk_device_realize(DeviceState *dev, Error **errp) -- Gitee From d41206e959717f68a31da4a2d875d33035baeb9f Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Mon, 29 Jul 2019 16:22:12 +0800 Subject: [PATCH 009/486] vhost: cancel migration when vhost-user restarted during migraiton Qemu will abort when vhost-user process is restarted during migration when vhost_log_global_start/stop is called. The reason is clear that vhost_dev_set_log returns -1 because network connection is temporarily lost. Let's cancel migraiton and report it to user in this abnormal situation. Signed-off-by: Ying Fang Reviewed-by: Gonglei --- hw/virtio/vhost.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 437347ad01..dafb23c481 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -25,6 +25,7 @@ #include "hw/virtio/virtio-access.h" #include "migration/blocker.h" #include "migration/qemu-file-types.h" +#include "migration/migration.h" #include "sysemu/dma.h" #include "sysemu/tcg.h" #include "trace.h" @@ -947,20 +948,24 @@ check_dev_state: static void vhost_log_global_start(MemoryListener *listener) { int r; + Error *errp = NULL; r = vhost_migration_log(listener, true); if (r < 0) { - abort(); + error_setg(&errp, "Failed to start vhost migration log"); + migrate_fd_error(migrate_get_current(), errp); } } static void vhost_log_global_stop(MemoryListener *listener) { int r; + Error *errp = NULL; r = vhost_migration_log(listener, false); if (r < 0) { - abort(); + error_setg(&errp, "Failed to stop vhost migration log"); + migrate_fd_error(migrate_get_current(), errp); } } -- Gitee From 39d851b5d5517fbcecc8d16229ae72ca152899b7 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Sat, 30 Jan 2021 14:57:54 +0800 Subject: [PATCH 010/486] migration: Add multi-thread compress method A multi-thread compress method parameter is added to hold the method we are going to use. By default the 'zlib' method is used to maintain the compatibility as before. Signed-off-by: Chuan Zheng Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang --- hw/core/qdev-properties-system.c | 11 +++++++++++ include/hw/qdev-properties.h | 4 ++++ migration/migration.c | 11 +++++++++++ monitor/hmp-cmds.c | 13 +++++++++++++ qapi/migration.json | 26 +++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a91f60567a..8c265bed6f 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1119,3 +1119,14 @@ const PropertyInfo qdev_prop_uuid = { .set = set_uuid, .set_default_value = set_default_uuid_auto, }; + +/* --- CompressMethod --- */ +const PropertyInfo qdev_prop_compress_method = { + .name = "CompressMethod", + .description = "multi-thread compression method, " + "zlib", + .enum_table = &CompressMethod_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index f7925f67d0..ea129d65a6 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -58,6 +58,7 @@ extern const PropertyInfo qdev_prop_int64; extern const PropertyInfo qdev_prop_size; extern const PropertyInfo qdev_prop_string; extern const PropertyInfo qdev_prop_on_off_auto; +extern const PropertyInfo qdev_prop_compress_method; extern const PropertyInfo qdev_prop_size32; extern const PropertyInfo qdev_prop_arraylen; extern const PropertyInfo qdev_prop_link; @@ -161,6 +162,9 @@ extern const PropertyInfo qdev_prop_link; DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) #define DEFINE_PROP_ON_OFF_AUTO(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_on_off_auto, OnOffAuto) +#define DEFINE_PROP_COMPRESS_METHOD(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_compress_method, \ + CompressMethod) #define DEFINE_PROP_SIZE32(_n, _s, _f, _d) \ DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size32, uint32_t) diff --git a/migration/migration.c b/migration/migration.c index abaf6f9e3d..fa3db87d75 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -83,6 +83,7 @@ #define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 /*0: means nocompress, 1: best speed, ... 9: best compress ratio */ #define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 +#define DEFAULT_MIGRATE_COMPRESS_METHOD COMPRESS_METHOD_ZLIB /* Define default autoconverge cpu throttle migration parameters */ #define DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD 50 #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 @@ -855,6 +856,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->compress_wait_thread = s->parameters.compress_wait_thread; params->has_decompress_threads = true; params->decompress_threads = s->parameters.decompress_threads; + params->has_compress_method = true; + params->compress_method = s->parameters.compress_method; params->has_throttle_trigger_threshold = true; params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold; params->has_cpu_throttle_initial = true; @@ -1491,6 +1494,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->decompress_threads = params->decompress_threads; } + if (params->has_compress_method) { + dest->compress_method = params->compress_method; + } + if (params->has_throttle_trigger_threshold) { dest->throttle_trigger_threshold = params->throttle_trigger_threshold; } @@ -4159,6 +4166,9 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, parameters.decompress_threads, DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), + DEFINE_PROP_COMPRESS_METHOD("compress-method", MigrationState, + parameters.compress_method, + DEFAULT_MIGRATE_COMPRESS_METHOD), DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, parameters.throttle_trigger_threshold, DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD), @@ -4275,6 +4285,7 @@ static void migration_instance_init(Object *obj) params->has_compress_level = true; params->has_compress_threads = true; params->has_decompress_threads = true; + params->has_compress_method = true; params->has_throttle_trigger_threshold = true; params->has_cpu_throttle_initial = true; params->has_cpu_throttle_increment = true; diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 9c91bf93e9..294652034e 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -45,6 +45,7 @@ #include "qapi/qapi-visit-net.h" #include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qdict.h" +#include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qerror.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" @@ -429,6 +430,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), params->decompress_threads); assert(params->has_throttle_trigger_threshold); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_METHOD), + CompressMethod_str(params->compress_method)); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), params->throttle_trigger_threshold); @@ -1191,6 +1195,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); uint64_t valuebw = 0; uint64_t cache_size; + CompressMethod compress_method; Error *err = NULL; int val, ret; @@ -1216,6 +1221,14 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_decompress_threads = true; visit_type_uint8(v, param, &p->decompress_threads, &err); break; + case MIGRATION_PARAMETER_COMPRESS_METHOD: + p->has_compress_method = true; + visit_type_CompressMethod(v, param, &compress_method, &err); + if (err) { + break; + } + p->compress_method = compress_method; + break; case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: p->has_throttle_trigger_threshold = true; visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err); diff --git a/qapi/migration.json b/qapi/migration.json index bbfd48cf0b..3a76907ea9 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -596,6 +596,19 @@ 'bitmaps': [ 'BitmapMigrationBitmapAlias' ] } } +## +# @CompressMethod: +# +# An enumeration of multi-thread compression methods. +# +# @zlib: use zlib compression method. +# +# Since: 5.0 +# +## +{ 'enum': 'CompressMethod', + 'data': [ 'zlib' ] } + ## # @MigrationParameter: # @@ -632,6 +645,9 @@ # compression, so set the decompress-threads to the number about 1/4 # of compress-threads is adequate. # +# @compress-method: Which multi-thread compression method to use. +# Defaults to none. (Since 5.0) +# # @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period # to trigger throttling. It is expressed as percentage. # The default value is 50. (Since 5.0) @@ -758,7 +774,7 @@ 'data': ['announce-initial', 'announce-max', 'announce-rounds', 'announce-step', 'compress-level', 'compress-threads', 'decompress-threads', - 'compress-wait-thread', 'throttle-trigger-threshold', + 'compress-wait-thread', 'compress-method', 'throttle-trigger-threshold', 'cpu-throttle-initial', 'cpu-throttle-increment', 'cpu-throttle-tailslow', 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', @@ -797,6 +813,9 @@ # # @decompress-threads: decompression thread count # +# @compress-method: Set compression method to use in multi-thread compression. +# Defaults to none. (Since 5.0) +# # @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period # to trigger throttling. It is expressed as percentage. # The default value is 50. (Since 5.0) @@ -930,6 +949,7 @@ '*compress-threads': 'uint8', '*compress-wait-thread': 'bool', '*decompress-threads': 'uint8', + '*compress-method': 'CompressMethod', '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -995,6 +1015,9 @@ # # @decompress-threads: decompression thread count # +# @compress-method: Which multi-thread compression method to use. +# Defaults to none. (Since 5.0) +# # @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period # to trigger throttling. It is expressed as percentage. # The default value is 50. (Since 5.0) @@ -1128,6 +1151,7 @@ '*compress-threads': 'uint8', '*compress-wait-thread': 'bool', '*decompress-threads': 'uint8', + '*compress-method': 'CompressMethod', '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', -- Gitee From b871594aa1798ddcc7f5124e5b3e1c5d858c155c Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Sat, 30 Jan 2021 15:21:17 +0800 Subject: [PATCH 011/486] migration: Refactoring multi-thread compress migration Code refactor for the compression procedure which includes: 1. Move qemu_compress_data and qemu_put_compression_data from qemu-file.c to ram.c, for the reason that most part of the code logical has nothing to do with qemu-file. Besides, the decompression code is located at ram.c only. 2. Simplify the function input arguments for compression and decompression. Wrap the input into the param structure which already exists. This change also makes the function much more flexible for other compression methods. Signed-off-by: Chuan Zheng Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang --- migration/qemu-file.c | 61 ++++++------------------------- migration/qemu-file.h | 4 +- migration/ram.c | 85 +++++++++++++++++++++++++++++++------------ 3 files changed, 75 insertions(+), 75 deletions(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 6338d8e2ff..e07026da4f 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -745,55 +745,6 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } -/* return the size after compression, or negative value on error */ -static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) -{ - int err; - - err = deflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = source_len; - stream->next_in = (uint8_t *)source; - stream->avail_out = dest_len; - stream->next_out = dest; - - err = deflate(stream, Z_FINISH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->next_out - dest; -} - -/* Compress size bytes of data start at p and store the compressed - * data to the buffer of f. - * - * Since the file is dummy file with empty_ops, return -1 if f has no space to - * save the compressed data. - */ -ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, - const uint8_t *p, size_t size) -{ - ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); - - if (blen < compressBound(size)) { - return -1; - } - - blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), - blen, p, size); - if (blen < 0) { - return -1; - } - - qemu_put_be32(f, blen); - add_buf_to_iovec(f, blen); - return blen + sizeof(int32_t); -} /* Put the data in the buffer of f_src to the buffer of f_des, and * then reset the buf_index of f_src to 0. @@ -866,3 +817,15 @@ QIOChannel *qemu_file_get_ioc(QEMUFile *file) { return file->has_ioc ? QIO_CHANNEL(file->opaque) : NULL; } + +ssize_t qemu_put_compress_start(QEMUFile *f, uint8_t **dest_ptr) +{ + *dest_ptr = f->buf + f->buf_index + sizeof(int32_t); + return IO_BUF_SIZE - f->buf_index - sizeof(int32_t); +} + +void qemu_put_compress_end(QEMUFile *f, unsigned int v) +{ + qemu_put_be32(f, v); + add_buf_to_iovec(f, v); +} diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 3f36d4dc8c..617a1373ad 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -139,8 +139,6 @@ bool qemu_file_is_writable(QEMUFile *f); size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); -ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, - const uint8_t *p, size_t size); int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); /* @@ -167,6 +165,8 @@ void ram_control_before_iterate(QEMUFile *f, uint64_t flags); void ram_control_after_iterate(QEMUFile *f, uint64_t flags); void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data); +ssize_t qemu_put_compress_start(QEMUFile *f, uint8_t **dest_ptr); +void qemu_put_compress_end(QEMUFile *f, unsigned int v); /* Whenever this is found in the data stream, the flags * will be passed to ram_control_load_hook in the incoming-migration * side. This lets before_ram_iterate/after_ram_iterate add diff --git a/migration/ram.c b/migration/ram.c index 863035d235..1176816fba 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -449,26 +449,22 @@ static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; static QemuCond decomp_done_cond; -static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf); +static bool do_compress_ram_page(CompressParam *param, RAMBlock *block); static void *do_data_compress(void *opaque) { CompressParam *param = opaque; RAMBlock *block; - ram_addr_t offset; bool zero_page; qemu_mutex_lock(¶m->mutex); while (!param->quit) { if (param->block) { block = param->block; - offset = param->offset; param->block = NULL; qemu_mutex_unlock(¶m->mutex); - zero_page = do_compress_ram_page(param->file, ¶m->stream, - block, offset, param->originbuf); + zero_page = do_compress_ram_page(param, block); qemu_mutex_lock(&comp_done_lock); param->done = true; @@ -1342,28 +1338,73 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, return 1; } -static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf) +/* + * Compress size bytes of data start at p and store the compressed + * data to the buffer of f. + * + * Since the file is dummy file with empty_ops, return -1 if f has no space to + * save the compressed data. + */ +static ssize_t qemu_put_compression_data(CompressParam *param, size_t size) +{ + int err; + uint8_t *dest = NULL; + z_stream *stream = ¶m->stream; + uint8_t *p = param->originbuf; + QEMUFile *f = f = param->file; + ssize_t blen = qemu_put_compress_start(f, &dest); + + if (blen < compressBound(size)) { + return -1; + } + + err = deflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = size; + stream->next_in = p; + stream->avail_out = blen; + stream->next_out = dest; + + err = deflate(stream, Z_FINISH); + if (err != Z_STREAM_END) { + return -1; + } + + blen = stream->next_out - dest; + if (blen < 0) { + return -1; + } + + qemu_put_compress_end(f, blen); + return blen + sizeof(int32_t); +} + +static bool do_compress_ram_page(CompressParam *param, RAMBlock *block) { RAMState *rs = ram_state; + ram_addr_t offset = param->offset; uint8_t *p = block->host + (offset & TARGET_PAGE_MASK); bool zero_page = false; int ret; - if (save_zero_page_to_file(rs, f, block, offset)) { + if (save_zero_page_to_file(rs, param->file, block, offset)) { zero_page = true; goto exit; } - save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); + save_page_header(rs, param->file, block, + offset | RAM_SAVE_FLAG_COMPRESS_PAGE); /* * copy it to a internal buffer to avoid it being modified by VM * so that we can catch up the error during compression and * decompression */ - memcpy(source_buf, p, TARGET_PAGE_SIZE); - ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); + memcpy(param->originbuf, p, TARGET_PAGE_SIZE); + ret = qemu_put_compression_data(param, TARGET_PAGE_SIZE); if (ret < 0) { qemu_file_set_error(migrate_get_current()->to_dst_file, ret); error_report("compressed data failed!"); @@ -3374,19 +3415,20 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) /* return the size after decompression, or negative value on error */ static int -qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) +qemu_uncompress_data(DecompressParam *param, uint8_t *dest, size_t pagesize) { int err; + z_stream *stream = ¶m->stream; + err = inflateReset(stream); if (err != Z_OK) { return -1; } - stream->avail_in = source_len; - stream->next_in = (uint8_t *)source; - stream->avail_out = dest_len; + stream->avail_in = param->len; + stream->next_in = param->compbuf; + stream->avail_out = pagesize; stream->next_out = dest; err = inflate(stream, Z_NO_FLUSH); @@ -3400,22 +3442,17 @@ qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, static void *do_data_decompress(void *opaque) { DecompressParam *param = opaque; - unsigned long pagesize; uint8_t *des; - int len, ret; + int ret; qemu_mutex_lock(¶m->mutex); while (!param->quit) { if (param->des) { des = param->des; - len = param->len; param->des = 0; qemu_mutex_unlock(¶m->mutex); - pagesize = TARGET_PAGE_SIZE; - - ret = qemu_uncompress_data(¶m->stream, des, pagesize, - param->compbuf, len); + ret = qemu_uncompress_data(param, des, TARGET_PAGE_SIZE); if (ret < 0 && migrate_get_current()->decompress_error_check) { error_report("decompress data failed"); qemu_file_set_error(decomp_file, ret); -- Gitee From 5e4bc7ceaf81a4932c92e479e9add947b698395b Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Sat, 30 Jan 2021 15:57:31 +0800 Subject: [PATCH 012/486] migration: Add multi-thread compress ops Add the MigrationCompressOps and MigrationDecompressOps structures to make the compression method configurable for multi-thread compression migration. Signed-off-by: Chuan Zheng Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang --- migration/migration.c | 9 ++ migration/migration.h | 1 + migration/ram.c | 269 ++++++++++++++++++++++++++++++------------ 3 files changed, 201 insertions(+), 78 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index fa3db87d75..07dc059251 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2456,6 +2456,15 @@ int migrate_decompress_threads(void) return s->parameters.decompress_threads; } +CompressMethod migrate_compress_method(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.compress_method; +} + bool migrate_dirty_bitmaps(void) { MigrationState *s; diff --git a/migration/migration.h b/migration/migration.h index 8130b703eb..4ed4f555da 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -355,6 +355,7 @@ int migrate_compress_level(void); int migrate_compress_threads(void); int migrate_compress_wait_thread(void); int migrate_decompress_threads(void); +CompressMethod migrate_compress_method(void); bool migrate_use_events(void); bool migrate_postcopy_blocktime(void); bool migrate_background_snapshot(void); diff --git a/migration/ram.c b/migration/ram.c index 1176816fba..069560e7f9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -417,6 +417,9 @@ struct CompressParam { /* internally used fields */ z_stream stream; uint8_t *originbuf; + + /* for zlib compression */ + z_stream stream; }; typedef struct CompressParam CompressParam; @@ -428,12 +431,29 @@ struct DecompressParam { void *des; uint8_t *compbuf; int len; + + /* for zlib compression */ z_stream stream; }; typedef struct DecompressParam DecompressParam; +typedef struct { + int (*save_setup)(CompressParam *param); + void (*save_cleanup)(CompressParam *param); + ssize_t (*compress_data)(CompressParam *param, size_t size); +} MigrationCompressOps; + +typedef struct { + int (*load_setup)(DecompressParam *param); + void (*load_cleanup)(DecompressParam *param); + int (*decompress_data)(DecompressParam *param, uint8_t *dest, size_t size); + int (*check_len)(int len); +} MigrationDecompressOps; + static CompressParam *comp_param; static QemuThread *compress_threads; +static MigrationCompressOps *compress_ops; +static MigrationDecompressOps *decompress_ops; /* comp_done_cond is used to wake up the migration thread when * one of the compression threads has finished the compression. * comp_done_lock is used to co-work with comp_done_cond. @@ -451,6 +471,157 @@ static QemuCond decomp_done_cond; static bool do_compress_ram_page(CompressParam *param, RAMBlock *block); +static int zlib_save_setup(CompressParam *param) +{ + if (deflateInit(¶m->stream, + migrate_compress_level()) != Z_OK) { + return -1; + } + + return 0; +} + +static ssize_t zlib_compress_data(CompressParam *param, size_t size) + + int err; + uint8_t *dest = NULL; + z_stream *stream = ¶m->stream; + uint8_t *p = param->originbuf; + QEMUFile *f = f = param->file; + ssize_t blen = qemu_put_compress_start(f, &dest); + + if (blen < compressBound(size)) { + return -1; + } + + err = deflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = size; + stream->next_in = p; + stream->avail_out = blen; + stream->next_out = dest; + + err = deflate(stream, Z_FINISH); + if (err != Z_STREAM_END) { + return -1; + } + + blen = stream->next_out - dest; + if (blen < 0) { + return -1; + } + + qemu_put_compress_end(f, blen); + return blen + sizeof(int32_t); +} + +static void zlib_save_cleanup(CompressParam *param) +{ + deflateEnd(¶m->stream); +} + +static int zlib_load_setup(DecompressParam *param) +{ + if (inflateInit(¶m->stream) != Z_OK) { + return -1; + } + + return 0; +} + +static int +zlib_decompress_data(DecompressParam *param, uint8_t *dest, size_t size) +{ + int err; + + z_stream *stream = ¶m->stream; + + err = inflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = param->len; + stream->next_in = param->compbuf; + stream->avail_out = size; + stream->next_out = dest; + + err = inflate(stream, Z_NO_FLUSH); + if (err != Z_STREAM_END) { + return -1; + } + + return stream->total_out; +} + +static void zlib_load_cleanup(DecompressParam *param) +{ + inflateEnd(¶m->stream); +} + +static int zlib_check_len(int len) +{ + return len < 0 || len > compressBound(TARGET_PAGE_SIZE); +} + +static int set_compress_ops(void) +{ + compress_ops = g_new0(MigrationCompressOps, 1); + + switch (migrate_compress_method()) { + case COMPRESS_METHOD_ZLIB: + compress_ops->save_setup = zlib_save_setup; + compress_ops->save_cleanup = zlib_save_cleanup; + compress_ops->compress_data = zlib_compress_data; + break; + default: + return -1; + } + + return 0; +} + +static int set_decompress_ops(void) +{ + decompress_ops = g_new0(MigrationDecompressOps, 1); + + switch (migrate_compress_method()) { + case COMPRESS_METHOD_ZLIB: + decompress_ops->load_setup = zlib_load_setup; + decompress_ops->load_cleanup = zlib_load_cleanup; + decompress_ops->decompress_data = zlib_decompress_data; + decompress_ops->check_len = zlib_check_len; + break; + default: + return -1; + } + + return 0; +} + +static void clean_compress_ops(void) +{ + compress_ops->save_setup = NULL; + compress_ops->save_cleanup = NULL; + compress_ops->compress_data = NULL; + + g_free(compress_ops); + compress_ops = NULL; +} + +static void clean_decompress_ops(void) +{ + decompress_ops->load_setup = NULL; + decompress_ops->load_cleanup = NULL; + decompress_ops->decompress_data = NULL; + + g_free(decompress_ops); + decompress_ops = NULL; +} + static void *do_data_compress(void *opaque) { CompressParam *param = opaque; @@ -508,7 +679,7 @@ static void compress_threads_save_cleanup(void) qemu_thread_join(compress_threads + i); qemu_mutex_destroy(&comp_param[i].mutex); qemu_cond_destroy(&comp_param[i].cond); - deflateEnd(&comp_param[i].stream); + compress_ops->save_cleanup(&comp_param[i]); g_free(comp_param[i].originbuf); qemu_fclose(comp_param[i].file); comp_param[i].file = NULL; @@ -519,6 +690,7 @@ static void compress_threads_save_cleanup(void) g_free(comp_param); compress_threads = NULL; comp_param = NULL; + clean_compress_ops(); } static int compress_threads_save_setup(void) @@ -528,6 +700,12 @@ static int compress_threads_save_setup(void) if (!migrate_use_compression()) { return 0; } + + if (set_compress_ops() < 0) { + clean_compress_ops(); + return -1; + } + thread_count = migrate_compress_threads(); compress_threads = g_new0(QemuThread, thread_count); comp_param = g_new0(CompressParam, thread_count); @@ -539,8 +717,7 @@ static int compress_threads_save_setup(void) goto exit; } - if (deflateInit(&comp_param[i].stream, - migrate_compress_level()) != Z_OK) { + if (compress_ops->save_setup(&comp_param[i]) < 0) { g_free(comp_param[i].originbuf); goto exit; } @@ -1338,50 +1515,6 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, return 1; } -/* - * Compress size bytes of data start at p and store the compressed - * data to the buffer of f. - * - * Since the file is dummy file with empty_ops, return -1 if f has no space to - * save the compressed data. - */ -static ssize_t qemu_put_compression_data(CompressParam *param, size_t size) -{ - int err; - uint8_t *dest = NULL; - z_stream *stream = ¶m->stream; - uint8_t *p = param->originbuf; - QEMUFile *f = f = param->file; - ssize_t blen = qemu_put_compress_start(f, &dest); - - if (blen < compressBound(size)) { - return -1; - } - - err = deflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = size; - stream->next_in = p; - stream->avail_out = blen; - stream->next_out = dest; - - err = deflate(stream, Z_FINISH); - if (err != Z_STREAM_END) { - return -1; - } - - blen = stream->next_out - dest; - if (blen < 0) { - return -1; - } - - qemu_put_compress_end(f, blen); - return blen + sizeof(int32_t); -} - static bool do_compress_ram_page(CompressParam *param, RAMBlock *block) { RAMState *rs = ram_state; @@ -1404,7 +1537,7 @@ static bool do_compress_ram_page(CompressParam *param, RAMBlock *block) * decompression */ memcpy(param->originbuf, p, TARGET_PAGE_SIZE); - ret = qemu_put_compression_data(param, TARGET_PAGE_SIZE); + ret = compress_ops->compress_data(param, TARGET_PAGE_SIZE); if (ret < 0) { qemu_file_set_error(migrate_get_current()->to_dst_file, ret); error_report("compressed data failed!"); @@ -3413,32 +3546,6 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) } } -/* return the size after decompression, or negative value on error */ -static int -qemu_uncompress_data(DecompressParam *param, uint8_t *dest, size_t pagesize) -{ - int err; - - z_stream *stream = ¶m->stream; - - err = inflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = param->len; - stream->next_in = param->compbuf; - stream->avail_out = pagesize; - stream->next_out = dest; - - err = inflate(stream, Z_NO_FLUSH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->total_out; -} - static void *do_data_decompress(void *opaque) { DecompressParam *param = opaque; @@ -3452,7 +3559,7 @@ static void *do_data_decompress(void *opaque) param->des = 0; qemu_mutex_unlock(¶m->mutex); - ret = qemu_uncompress_data(param, des, TARGET_PAGE_SIZE); + ret = decompress_ops->decompress_data(param, des, TARGET_PAGE_SIZE); if (ret < 0 && migrate_get_current()->decompress_error_check) { error_report("decompress data failed"); qemu_file_set_error(decomp_file, ret); @@ -3522,7 +3629,7 @@ static void compress_threads_load_cleanup(void) qemu_thread_join(decompress_threads + i); qemu_mutex_destroy(&decomp_param[i].mutex); qemu_cond_destroy(&decomp_param[i].cond); - inflateEnd(&decomp_param[i].stream); + decompress_ops->load_cleanup(&decomp_param[i]); g_free(decomp_param[i].compbuf); decomp_param[i].compbuf = NULL; } @@ -3531,6 +3638,7 @@ static void compress_threads_load_cleanup(void) decompress_threads = NULL; decomp_param = NULL; decomp_file = NULL; + clean_decompress_ops(); } static int compress_threads_load_setup(QEMUFile *f) @@ -3541,6 +3649,11 @@ static int compress_threads_load_setup(QEMUFile *f) return 0; } + if (set_decompress_ops() < 0) { + clean_decompress_ops(); + return -1; + } + thread_count = migrate_decompress_threads(); decompress_threads = g_new0(QemuThread, thread_count); decomp_param = g_new0(DecompressParam, thread_count); @@ -3548,7 +3661,7 @@ static int compress_threads_load_setup(QEMUFile *f) qemu_cond_init(&decomp_done_cond); decomp_file = f; for (i = 0; i < thread_count; i++) { - if (inflateInit(&decomp_param[i].stream) != Z_OK) { + if (decompress_ops->load_setup(&decomp_param[i]) < 0) { goto exit; } @@ -4156,7 +4269,7 @@ static int ram_load_precopy(QEMUFile *f) case RAM_SAVE_FLAG_COMPRESS_PAGE: len = qemu_get_be32(f); - if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { + if (decompress_ops->check_len(len)) { error_report("Invalid compressed data length: %d", len); ret = -EINVAL; break; -- Gitee From bafba05f7405ba31213120d99679cc4b6c1be68e Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Sat, 30 Jan 2021 16:15:10 +0800 Subject: [PATCH 013/486] migration: Add zstd support in multi-thread compression This patch enables zstd option in multi-thread compression. Signed-off-by: Chuan Zheng Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang --- hw/core/qdev-properties-system.c | 2 +- migration/ram.c | 131 ++++++++++++++++++++++++++++++- qapi/migration.json | 3 +- 3 files changed, 132 insertions(+), 4 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 8c265bed6f..6a6ff03be7 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1124,7 +1124,7 @@ const PropertyInfo qdev_prop_uuid = { const PropertyInfo qdev_prop_compress_method = { .name = "CompressMethod", .description = "multi-thread compression method, " - "zlib", + "zlib/zstd", .enum_table = &CompressMethod_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, diff --git a/migration/ram.c b/migration/ram.c index 069560e7f9..c3484ee1a9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -62,6 +62,11 @@ #include "qemu/userfaultfd.h" #endif /* defined(__linux__) */ +#ifdef CONFIG_ZSTD +#include +#include +#endif + /***********************************************************/ /* ram save/restore */ @@ -415,11 +420,16 @@ struct CompressParam { ram_addr_t offset; /* internally used fields */ - z_stream stream; uint8_t *originbuf; /* for zlib compression */ z_stream stream; + +#ifdef CONFIG_ZSTD + ZSTD_CStream *zstd_cs; + ZSTD_inBuffer in; + ZSTD_outBuffer out; +#endif }; typedef struct CompressParam CompressParam; @@ -434,6 +444,11 @@ struct DecompressParam { /* for zlib compression */ z_stream stream; +#ifdef CONFIG_ZSTD + ZSTD_DStream *zstd_ds; + ZSTD_inBuffer in; + ZSTD_outBuffer out; +#endif }; typedef struct DecompressParam DecompressParam; @@ -482,7 +497,7 @@ static int zlib_save_setup(CompressParam *param) } static ssize_t zlib_compress_data(CompressParam *param, size_t size) - +{ int err; uint8_t *dest = NULL; z_stream *stream = ¶m->stream; @@ -567,6 +582,103 @@ static int zlib_check_len(int len) return len < 0 || len > compressBound(TARGET_PAGE_SIZE); } +#ifdef CONFIG_ZSTD +static int zstd_save_setup(CompressParam *param) +{ + int res; + param->zstd_cs = ZSTD_createCStream(); + if (!param->zstd_cs) { + return -1; + } + res = ZSTD_initCStream(param->zstd_cs, migrate_compress_level()); + if (ZSTD_isError(res)) { + return -1; + } + return 0; +} +static void zstd_save_cleanup(CompressParam *param) +{ + ZSTD_freeCStream(param->zstd_cs); + param->zstd_cs = NULL; +} +static ssize_t zstd_compress_data(CompressParam *param, size_t size) +{ + int ret; + uint8_t *dest = NULL; + uint8_t *p = param->originbuf; + QEMUFile *f = f = param->file; + ssize_t blen = qemu_put_compress_start(f, &dest); + if (blen < ZSTD_compressBound(size)) { + return -1; + } + param->out.dst = dest; + param->out.size = blen; + param->out.pos = 0; + param->in.src = p; + param->in.size = size; + param->in.pos = 0; + do { + ret = ZSTD_compressStream2(param->zstd_cs, ¶m->out, + ¶m->in, ZSTD_e_end); + } while (ret > 0 && (param->in.size - param->in.pos > 0) + && (param->out.size - param->out.pos > 0)); + if (ret > 0 && (param->in.size - param->in.pos > 0)) { + return -1; + } + if (ZSTD_isError(ret)) { + return -1; + } + blen = param->out.pos; + qemu_put_compress_end(f, blen); + return blen + sizeof(int32_t); +} + +static int zstd_load_setup(DecompressParam *param) +{ + int ret; + param->zstd_ds = ZSTD_createDStream(); + if (!param->zstd_ds) { + return -1; + } + ret = ZSTD_initDStream(param->zstd_ds); + if (ZSTD_isError(ret)) { + return -1; + } + return 0; +} +static void zstd_load_cleanup(DecompressParam *param) +{ + ZSTD_freeDStream(param->zstd_ds); + param->zstd_ds = NULL; +} +static int +zstd_decompress_data(DecompressParam *param, uint8_t *dest, size_t size) +{ + int ret; + param->out.dst = dest; + param->out.size = size; + param->out.pos = 0; + param->in.src = param->compbuf; + param->in.size = param->len; + param->in.pos = 0; + do { + ret = ZSTD_decompressStream(param->zstd_ds, ¶m->out, ¶m->in); + } while (ret > 0 && (param->in.size - param->in.pos > 0) + && (param->out.size - param->out.pos > 0)); + if (ret > 0 && (param->in.size - param->in.pos > 0)) { + return -1; + } + if (ZSTD_isError(ret)) { + return -1; + } + return ret; +} +static int zstd_check_len(int len) +{ + return len < 0 || len > ZSTD_compressBound(TARGET_PAGE_SIZE); +} +#endif + static int set_compress_ops(void) { compress_ops = g_new0(MigrationCompressOps, 1); @@ -577,6 +689,13 @@ static int set_compress_ops(void) compress_ops->save_cleanup = zlib_save_cleanup; compress_ops->compress_data = zlib_compress_data; break; +#ifdef CONFIG_ZSTD + case COMPRESS_METHOD_ZSTD: + compress_ops->save_setup = zstd_save_setup; + compress_ops->save_cleanup = zstd_save_cleanup; + compress_ops->compress_data = zstd_compress_data; + break; +#endif default: return -1; } @@ -595,6 +714,14 @@ static int set_decompress_ops(void) decompress_ops->decompress_data = zlib_decompress_data; decompress_ops->check_len = zlib_check_len; break; +#ifdef CONFIG_ZSTD + case COMPRESS_METHOD_ZSTD: + decompress_ops->load_setup = zstd_load_setup; + decompress_ops->load_cleanup = zstd_load_cleanup; + decompress_ops->decompress_data = zstd_decompress_data; + decompress_ops->check_len = zstd_check_len; + break; +#endif default: return -1; } diff --git a/qapi/migration.json b/qapi/migration.json index 3a76907ea9..d4ebc5f028 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -602,12 +602,13 @@ # An enumeration of multi-thread compression methods. # # @zlib: use zlib compression method. +# @zstd: use zstd compression method. # # Since: 5.0 # ## { 'enum': 'CompressMethod', - 'data': [ 'zlib' ] } + 'data': [ 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } ## # @MigrationParameter: -- Gitee From 84780210ac31e430d59b0c6d3d9f522c626b6380 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Sat, 30 Jan 2021 16:23:15 +0800 Subject: [PATCH 014/486] migration: Add compress_level sanity check Zlib compression has level from 1 to 9. However Zstd compression has level from 1 to 22 (level >= 20 not recommanded). Let's do sanity check here to make sure a vaild compress_level is given by user. Signed-off-by: Chuan Zheng Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang --- migration/migration.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 07dc059251..f86dd8cccd 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1320,14 +1320,40 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, } } +static bool compress_level_check(MigrationParameters *params, Error **errp) +{ + switch (params->compress_method) { + case COMPRESS_METHOD_ZLIB: + if (params->compress_level > 9 || params->compress_level < 1) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", + "a value in the range of 0 to 9 for Zlib method"); + return false; + } + break; +#ifdef CONFIG_ZSTD + case COMPRESS_METHOD_ZSTD: + if (params->compress_level > 19 || params->compress_level < 1) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", + "a value in the range of 1 to 19 for Zstd method"); + return false; + } + break; +#endif + default: + error_setg(errp, "Checking compress_level failed for unknown reason"); + return false; + } + + return true; +} + /* * Check whether the parameters are valid. Error will be put into errp * (if provided). Return true if valid, otherwise false. */ static bool migrate_params_check(MigrationParameters *params, Error **errp) { - if (params->has_compress_level && - (params->compress_level > 9)) { + if (params->has_compress_level && !compress_level_check(params, errp)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", "a value between 0 and 9"); return false; -- Gitee From 213bd45d2c5337f10216c69c13f0438dd40c58d8 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Sat, 30 Jan 2021 16:36:47 +0800 Subject: [PATCH 015/486] doc: Update multi-thread compression doc Modify the doc to fit the previous changes. Signed-off-by: Chuan Zheng Signed-off-by: Zeyu Jin Signed-off-by: Ying Fang --- docs/multi-thread-compression.txt | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/multi-thread-compression.txt b/docs/multi-thread-compression.txt index bb88c6bdf1..d429963cb0 100644 --- a/docs/multi-thread-compression.txt +++ b/docs/multi-thread-compression.txt @@ -33,14 +33,15 @@ thread compression can be used to accelerate the compression process. The decompression speed of Zlib is at least 4 times as quick as compression, if the source and destination CPU have equal speed, -keeping the compression thread count 4 times the decompression -thread count can avoid resource waste. +and you choose Zlib as compression method, keeping the compression +thread count 4 times the decompression thread count can avoid resource waste. Compression level can be used to control the compression speed and the -compression ratio. High compression ratio will take more time, level 0 -stands for no compression, level 1 stands for the best compression -speed, and level 9 stands for the best compression ratio. Users can -select a level number between 0 and 9. +compression ratio. High compression ratio will take more time, +level 1 stands for the best compression speed, and higher level means higher +compression ration. For Zlib, users can select a level number between 0 and 9, +where level 0 stands for no compression. For Zstd, users can select a +level number between 1 and 22. When to use the multiple thread compression in live migration @@ -116,16 +117,19 @@ to support the multiple thread compression migration: 2. Activate compression on the source: {qemu} migrate_set_capability compress on -3. Set the compression thread count on source: +3. Set the compression method: + {qemu} migrate_set_parameter compress_method zstd + +4. Set the compression thread count on source: {qemu} migrate_set_parameter compress_threads 12 -4. Set the compression level on the source: +5. Set the compression level on the source: {qemu} migrate_set_parameter compress_level 1 -5. Set the decompression thread count on destination: +6. Set the decompression thread count on destination: {qemu} migrate_set_parameter decompress_threads 3 -6. Start outgoing migration: +7. Start outgoing migration: {qemu} migrate -d tcp:destination.host:4444 {qemu} info migrate Capabilities: ... compress: on @@ -136,6 +140,7 @@ The following are the default settings: compress_threads: 8 decompress_threads: 2 compress_level: 1 (which means best speed) + compress_method: zlib So, only the first two steps are required to use the multiple thread compression in migration. You can do more if the default @@ -143,7 +148,7 @@ settings are not appropriate. TODO ==== -Some faster (de)compression method such as LZ4 and Quicklz can help -to reduce the CPU consumption when doing (de)compression. If using -these faster (de)compression method, less (de)compression threads +Comparing to Zlib, Some faster (de)compression method such as LZ4 +and Quicklz can help to reduce the CPU consumption when doing (de)compression. +If using these faster (de)compression method, less (de)compression threads are needed when doing the migration. -- Gitee From ef83cde8dd2c9b404527354489b14d2bd238733d Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Tue, 8 Feb 2022 20:48:17 +0800 Subject: [PATCH 016/486] cpu: parse +/- feature to avoid failure To avoid cpu feature parse failure, +/- feature is added. Signed-off-by: Xu Yandong Signed-off-by: Mingwang Li --- target/arm/cpu64.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 15245a60a8..019edc66c9 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -933,10 +933,47 @@ static gchar *aarch64_gdb_arch_name(CPUState *cs) return g_strdup("aarch64"); } +/* Parse "+feature,-feature,feature=foo" CPU feature string + */ +static void arm_cpu_parse_featurestr(const char *typename, char *features, + Error **errp ) +{ + char *featurestr; + char *val; + static bool cpu_globals_initialized; + + if (cpu_globals_initialized) { + return; + } + cpu_globals_initialized = true; + + featurestr = features ? strtok(features, ",") : NULL; + while (featurestr) { + val = strchr(featurestr, '='); + if (val) { + GlobalProperty *prop = g_new0(typeof(*prop), 1); + *val = 0; + val++; + prop->driver = typename; + prop->property = g_strdup(featurestr); + prop->value = g_strdup(val); + qdev_prop_register_global(prop); + } else if (featurestr[0] == '+' || featurestr[0] == '-') { + warn_report("Ignore %s feature\n", featurestr); + } else { + error_setg(errp, "Expected key=value format, found %s.", + featurestr); + return; + } + featurestr = strtok(NULL, ","); + } +} + static void aarch64_cpu_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); + cc->parse_features = arm_cpu_parse_featurestr; cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; cc->gdb_num_core_regs = 34; -- Gitee From 8ebab06c4824626ab4d7204133cd1e7b9c67f468 Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Tue, 8 Feb 2022 21:36:22 +0800 Subject: [PATCH 017/486] cpu: add Kunpeng-920 cpu support Add the Kunpeng-920 CPU model Signed-off-by: Xu Yandong Signed-off-by: Mingwang Li --- hw/arm/virt.c | 1 + target/arm/cpu64.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 30da05dfe0..a4a35584e9 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -201,6 +201,7 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), ARM_CPU_TYPE_NAME("cortex-a72"), + ARM_CPU_TYPE_NAME("Kunpeng-920"), ARM_CPU_TYPE_NAME("a64fx"), ARM_CPU_TYPE_NAME("host"), ARM_CPU_TYPE_NAME("max"), diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 019edc66c9..aaca79f7c3 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -248,6 +248,26 @@ static void aarch64_a72_initfn(Object *obj) define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo); } +static void aarch64_kunpeng_920_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + /* + * Hisilicon Kunpeng-920 CPU is similar to cortex-a72, + * so first initialize cpu data as cortex-a72, + * and then update the special register. + */ + aarch64_a72_initfn(obj); + + cpu->midr = 0x480fd010; + cpu->ctr = 0x84448004; + cpu->isar.id_aa64pfr0 = 0x11001111; + cpu->isar.id_aa64dfr0 = 0x110305408; + cpu->isar.id_aa64isar0 = 0x10211120; + cpu->isar.id_aa64mmfr0 = 0x101125; + cpu->kvm_target = KVM_ARM_TARGET_GENERIC_V8; +} + void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { /* @@ -892,6 +912,7 @@ static const ARMCPUInfo aarch64_cpus[] = { { .name = "cortex-a57", .initfn = aarch64_a57_initfn }, { .name = "cortex-a53", .initfn = aarch64_a53_initfn }, { .name = "cortex-a72", .initfn = aarch64_a72_initfn }, + { .name = "Kunpeng-920", .initfn = aarch64_kunpeng_920_initfn}, { .name = "a64fx", .initfn = aarch64_a64fx_initfn }, { .name = "max", .initfn = aarch64_max_initfn }, }; -- Gitee From f0da7fa5230b5f771570b2c12288e4a56a20dd97 Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Tue, 8 Feb 2022 22:18:55 +0800 Subject: [PATCH 018/486] cpu: add Cortex-A72 processor kvm target support The ARM Cortex-A72 is ARMv8-A micro-architecture, add kvm target to ARM Cortex-A72 processor definition. Signed-off-by: Xu Yandong Signed-off-by: Mingwang Li --- target/arm/cpu64.c | 1 + target/arm/kvm-consts.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index aaca79f7c3..556b6f3691 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -202,6 +202,7 @@ static void aarch64_a72_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a72"; + cpu->kvm_target = QEMU_KVM_ARM_TARGET_GENERIC_V8; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 580f1c1fee..5f1311ade7 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -130,6 +130,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED); #define QEMU_KVM_ARM_TARGET_CORTEX_A57 2 #define QEMU_KVM_ARM_TARGET_XGENE_POTENZA 3 #define QEMU_KVM_ARM_TARGET_CORTEX_A53 4 +/* Generic ARM v8 target */ +#define QEMU_KVM_ARM_TARGET_GENERIC_V8 5 /* There's no kernel define for this: sentinel value which * matches no KVM target value for either 64 or 32 bit @@ -141,6 +143,7 @@ MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53); +MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_GENERIC_V8, KVM_ARM_TARGET_GENERIC_V8); #define CP_REG_ARM64 0x6000000000000000ULL #define CP_REG_ARM_COPROC_MASK 0x000000000FFF0000 -- Gitee From ec35c96006851a956a7e401f29af0ffe137c4bb9 Mon Sep 17 00:00:00 2001 From: Jiadong Zeng Date: Tue, 8 Feb 2022 22:56:37 +0800 Subject: [PATCH 019/486] add Phytium's CPU models: FT-2000+ and Tengyun-S2500. Signed-off-by: Jiadong Zeng Signed-off-by: Mingwang Li --- hw/arm/virt.c | 2 ++ target/arm/cpu64.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a4a35584e9..3c972fdab0 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -202,6 +202,8 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a57"), ARM_CPU_TYPE_NAME("cortex-a72"), ARM_CPU_TYPE_NAME("Kunpeng-920"), + ARM_CPU_TYPE_NAME("FT-2000+"), + ARM_CPU_TYPE_NAME("Tengyun-S2500"), ARM_CPU_TYPE_NAME("a64fx"), ARM_CPU_TYPE_NAME("host"), ARM_CPU_TYPE_NAME("max"), diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 556b6f3691..08d886de7b 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -676,6 +676,32 @@ static Property arm_cpu_pauth_property = static Property arm_cpu_pauth_impdef_property = DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false); +static void aarch64_max_ft2000plus_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + if (kvm_enabled()) { + kvm_arm_set_cpu_features_from_host(cpu); + kvm_arm_add_vcpu_properties(obj); + } else { + aarch64_a72_initfn(obj); + cpu->midr = 0x70186622; + } +} + +static void aarch64_max_tengyun_s2500_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + if (kvm_enabled()) { + kvm_arm_set_cpu_features_from_host(cpu); + kvm_arm_add_vcpu_properties(obj); + } else { + aarch64_a72_initfn(obj); + cpu->midr = 0x70186632; + } +} + /* -cpu max: if KVM is enabled, like -cpu host (best possible with this host); * otherwise, a CPU with as many features enabled as our emulation supports. * The version of '-cpu max' for qemu-system-arm is defined in cpu.c; @@ -914,6 +940,8 @@ static const ARMCPUInfo aarch64_cpus[] = { { .name = "cortex-a53", .initfn = aarch64_a53_initfn }, { .name = "cortex-a72", .initfn = aarch64_a72_initfn }, { .name = "Kunpeng-920", .initfn = aarch64_kunpeng_920_initfn}, + { .name = "FT-2000+", .initfn = aarch64_max_ft2000plus_initfn }, + { .name = "Tengyun-S2500", .initfn = aarch64_max_tengyun_s2500_initfn }, { .name = "a64fx", .initfn = aarch64_a64fx_initfn }, { .name = "max", .initfn = aarch64_max_initfn }, }; -- Gitee From d9fef6139e17976db194d73848baff543c4a2590 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Wed, 9 Feb 2022 08:49:41 +0800 Subject: [PATCH 020/486] migration: skip cache_drop for bios bootloader and nvram template Qemu enabled page cache dropping for raw device on the destionation host during shared storage migration. However, fsync may take 300ms to multiple seconds to return in multiple-migration scene, because all domains in a host share bios bootloader file, skip cache_drop for bios bootloader and nvram template to avoid downtime increase. --- block.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index 0ac5b163d2..91f123a354 100644 --- a/block.c +++ b/block.c @@ -67,6 +67,9 @@ #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ +#define DEFAULT_BIOS_BOOT_LOADER_DIR "/usr/share/edk2" +#define DEFAULT_NVRAM_TEMPLATE_DIR "/var/lib/libvirt/qemu/nvram" + static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); @@ -6432,7 +6435,13 @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) return ret; } - if (bs->drv->bdrv_co_invalidate_cache) { + /* + * It's not necessary for bios bootloader and nvram template to drop cache + * when migration, skip this step for them to avoid dowtime increase. + */ + if (bs->drv->bdrv_co_invalidate_cache && + !strstr(bs->filename, DEFAULT_BIOS_BOOT_LOADER_DIR) && + !strstr(bs->filename, DEFAULT_NVRAM_TEMPLATE_DIR)) { bs->drv->bdrv_co_invalidate_cache(bs, &local_err); if (local_err) { bs->open_flags |= BDRV_O_INACTIVE; -- Gitee From 3cb1b0ce091998532a30793e3272925da4e6f3aa Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Mon, 7 Feb 2022 14:31:34 +0800 Subject: [PATCH 021/486] hugepages: hugepages files maybe leftover Before qemu uses the hugepage memory directory /dev/hugepages/libvirt/qemu/xxx, The directory may be deleted because of the destroy virtual machine. Cause qemu to create files directly under /dev/hugepages/libvirt/qemu/. After the file is created, the file is not cleaned up by unlink, and when the virtual machine is destroyed, libvirt will only clean up /dev/hugepages/libvirt/qemu/xxx directory. After creating the hugepage file, execute unlink to clean up the file to fix the problem. Signed-off-by: Jinhua Cao Signed-off-by: Jiajie Li --- include/qemu/mmap-alloc.h | 3 +++ softmmu/physmem.c | 10 +++++++++- util/mmap-alloc.c | 22 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h index 90d0eee705..707202e5be 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -1,6 +1,9 @@ #ifndef QEMU_MMAP_ALLOC_H #define QEMU_MMAP_ALLOC_H +#define HUGETLBFS_MAGIC 0x958458f6 + +size_t qemu_fd_getfiletype(int fd); size_t qemu_fd_getpagesize(int fd); diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 3524c04c2a..3b9a61448c 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1496,7 +1496,14 @@ static int file_ram_open(const char *path, /* @path names a file that doesn't exist, create it */ fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644); if (fd >= 0) { - *created = true; + info_report("open %s success \n", path); + /* if fd file type is HUGETLBFS_MAGIC, unlink it, */ + /* in case to prevent residue after qemu killed */ + if (qemu_fd_getfiletype(fd) == HUGETLBFS_MAGIC) { + unlink(path); + } else { + *created = true; + } break; } } else if (errno == EISDIR) { @@ -1515,6 +1522,7 @@ static int file_ram_open(const char *path, fd = mkstemp(filename); if (fd >= 0) { + info_report("mkstemp %s success \n", filename); unlink(filename); g_free(filename); break; diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 893d864354..4993dd5bfa 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -29,6 +29,28 @@ #include #endif +size_t qemu_fd_getfiletype(int fd) +{ + struct statfs fs; + int ret; + + if (fd != -1) { + do { + ret = fstatfs(fd, &fs); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + fprintf(stderr, "Couldn't fstatfs() fd: %s\n", + strerror(errno)); + return -1; + } + return fs.f_type; + } else { + fprintf(stderr, "fd is invalid \n"); + return -1; + } +} + size_t qemu_fd_getpagesize(int fd) { #ifdef CONFIG_LINUX -- Gitee From a09c3928b33b0c53831bd9eeb56f8171c26057bc Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 09:46:53 +0800 Subject: [PATCH 022/486] target-i386: Modify the VM's physical bits value set policy. To resolve the problem that a VM with large memory capacity fails to be live migrated, determine whether the VM is a large memory capacity based on the memory size (4 TB). If yes, set the bus width of the VM address to 46 bits. If no, set the bus width to 42 bits. Signed-off-by: Jinhua Cao Signed-off-by: Jiajie Li --- target/i386/cpu.c | 19 ++++++++++++++++++- target/i386/cpu.h | 6 ++++++ target/i386/host-cpu.c | 13 +++++++------ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index aa9e636800..868cf3e7e8 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6678,6 +6678,23 @@ static void x86_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.eip = value; } +/* At present, we check the vm is *LARGE* or not, i.e. whether + * the memory size is more than 4T or not. + */ +const uint64_t large_vm_mem_size = 0x40000000000UL; +void x86_cpu_adjuest_by_ram_size(ram_addr_t ram_size, X86CPU *cpu) +{ + /* If there is not a large vm, we set the phys_bits to 42 bits, + * otherwise, we increase the phys_bits to 46 bits. + */ + if (ram_size < large_vm_mem_size) { + cpu->phys_bits = DEFAULT_VM_CPU_PHYS_BITS; + } else { + cpu->phys_bits = LARGE_VM_CPU_PHYS_BITS; + cpu->fill_mtrr_mask = true; + } +} + int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); @@ -6862,7 +6879,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0), DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0), - DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), + DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, false), DEFINE_PROP_UINT32("level-func7", X86CPU, env.cpuid_level_func7, UINT32_MAX), DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, UINT32_MAX), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 04f2b790c9..6f777fd6ca 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -24,6 +24,7 @@ #include "cpu-qom.h" #include "kvm/hyperv-proto.h" #include "exec/cpu-defs.h" +#include "exec/cpu-common.h" #include "qapi/qapi-types-common.h" /* The x86 has a strong memory model with some store-after-load re-ordering */ @@ -1841,6 +1842,11 @@ struct X86CPU { extern const VMStateDescription vmstate_x86_cpu; #endif +#define DEFAULT_VM_CPU_PHYS_BITS 42 +#define LARGE_VM_CPU_PHYS_BITS 46 + +void x86_cpu_adjuest_by_ram_size(ram_addr_t ram_size, X86CPU *cpu); + int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 10f8aba86e..5a1bbefa36 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -12,6 +12,7 @@ #include "host-cpu.h" #include "qapi/error.h" #include "sysemu/sysemu.h" +#include "hw/boards.h" /* Note: Only safe for use on x86(-64) hosts */ static uint32_t host_cpu_phys_bits(void) @@ -56,14 +57,14 @@ static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu) uint32_t phys_bits = cpu->phys_bits; static bool warned; - /* - * Print a warning if the user set it to a value that's not the - * host value. - */ - if (phys_bits != host_phys_bits && phys_bits != 0 && + /* adjust x86 cpu phys_bits according to ram_size. */ + x86_cpu_adjuest_by_ram_size(current_machine->ram_size, cpu); + + /* Print a warning if the host value less than the user set. */ + if (phys_bits > host_phys_bits && phys_bits != 0 && !warned) { warn_report("Host physical bits (%u)" - " does not match phys-bits property (%u)", + " less than phys-bits property (%u)", host_phys_bits, phys_bits); warned = true; } -- Gitee From 391dd8f1458c8db0b848450718af5c69285e5705 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:54 +0800 Subject: [PATCH 023/486] scsi-bus: Refactor the code that retries requests Move the code that retries requests from scsi_dma_restart_bh() to its own, non-static, function. This will allow us to call it from the retry_request_cb() of scsi-disk in a future patch. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- hw/scsi/scsi-bus.c | 16 +++++++++++----- include/hw/scsi/scsi.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 77325d8cc7..5e6f891b9d 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -143,14 +143,10 @@ void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host, qbus_set_bus_hotplug_handler(BUS(bus)); } -static void scsi_dma_restart_bh(void *opaque) +void scsi_retry_requests(SCSIDevice *s) { - SCSIDevice *s = opaque; SCSIRequest *req, *next; - qemu_bh_delete(s->bh); - s->bh = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { scsi_req_ref(req); @@ -174,6 +170,16 @@ static void scsi_dma_restart_bh(void *opaque) object_unref(OBJECT(s)); } +static void scsi_dma_restart_bh(void *opaque) +{ + SCSIDevice *s = opaque; + + qemu_bh_delete(s->bh); + s->bh = NULL; + + scsi_retry_requests(s); +} + void scsi_req_retry(SCSIRequest *req) { /* No need to save a reference, because scsi_dma_restart_bh just diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index a567a5ed86..e5d90cd9dc 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -212,6 +212,7 @@ void scsi_req_cancel_complete(SCSIRequest *req); void scsi_req_cancel(SCSIRequest *req); void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier); void scsi_req_retry(SCSIRequest *req); +void scsi_retry_requests(SCSIDevice *s); void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); -- Gitee From 52115ca0ad925b1d719eb46e22c455aa5839534a Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 21 Jan 2021 15:46:55 +0800 Subject: [PATCH 024/486] scsi-disk: Add support for retry on errors Mark failed requests as to be retried and implement retry_request_cb to handle these requests. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- hw/scsi/scsi-disk.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index d4914178ea..d278efc701 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -246,6 +246,10 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) scsi_req_retry(&r->req); return true; + case BLOCK_ERROR_ACTION_RETRY: + scsi_req_retry(&r->req); + return true; + default: g_assert_not_reached(); } @@ -253,6 +257,8 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); return true; @@ -262,6 +268,7 @@ static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) return scsi_handle_rw_error(r, ret, acct_failed); } + blk_error_retry_reset_timeout(s->qdev.conf.blk); return false; } @@ -2278,6 +2285,13 @@ static void scsi_disk_resize_cb(void *opaque) } } +static void scsi_disk_retry_request(void *opaque) +{ + SCSIDiskState *s = opaque; + + scsi_retry_requests(&s->qdev); +} + static void scsi_cd_change_media_cb(void *opaque, bool load, Error **errp) { SCSIDiskState *s = opaque; @@ -2326,10 +2340,12 @@ static const BlockDevOps scsi_disk_removable_block_ops = { .is_medium_locked = scsi_cd_is_medium_locked, .resize_cb = scsi_disk_resize_cb, + .retry_request_cb = scsi_disk_retry_request, }; static const BlockDevOps scsi_disk_block_ops = { .resize_cb = scsi_disk_resize_cb, + .retry_request_cb = scsi_disk_retry_request, }; static void scsi_disk_unit_attention_reported(SCSIDevice *dev) -- Gitee From 06db37983cfd20d7e92001ac3cb06867a281f1c9 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 25 Feb 2021 18:03:57 +0800 Subject: [PATCH 025/486] block-backend: Stop retrying when draining Retrying failed requests when draining would make the draining hung. So it is better not to trigger the retry timer when draining. And after the virtual devices go back to work, they would retry those queued requests. Signed-off-by: Jiahui Cen Signed-off-by: Ying Fang Signed-off-by: Alex Chen --- block/block-backend.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index d3d90a95a5..49d236b2a4 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1874,9 +1874,11 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, send_qmp_error_event(blk, action, is_read, error); qemu_system_vmstop_request(RUN_STATE_IO_ERROR); } else if (action == BLOCK_ERROR_ACTION_RETRY) { - timer_mod(blk->retry_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + - blk->retry_interval); - send_qmp_error_event(blk, action, is_read, error); + if (!blk->quiesce_counter) { + timer_mod(blk->retry_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + + blk->retry_interval); + send_qmp_error_event(blk, action, is_read, error); + } } else { send_qmp_error_event(blk, action, is_read, error); } -- Gitee From 19523565181bb6efb1b9f819d45a7ca8ea6eca19 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Wed, 9 Feb 2022 11:21:28 +0800 Subject: [PATCH 026/486] ps2: fix oob in ps2 kbd fix oob in ps2 kbd --- hw/input/ps2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 9376a8f4ce..5d82ee3cdf 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -205,7 +205,7 @@ void ps2_queue_noirq(PS2State *s, int b) } q->data[q->wptr] = b; - if (++q->wptr == PS2_BUFFER_SIZE) { + if (++q->wptr >= PS2_BUFFER_SIZE) { q->wptr = 0; } q->count++; @@ -578,7 +578,7 @@ uint32_t ps2_read_data(PS2State *s) val = q->data[index]; } else { val = q->data[q->rptr]; - if (++q->rptr == PS2_BUFFER_SIZE) { + if (++q->rptr >= PS2_BUFFER_SIZE) { q->rptr = 0; } q->count--; -- Gitee From 9e2158495059b485f2c28bb649c8b4a87fb3105c Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Wed, 9 Feb 2022 11:24:32 +0800 Subject: [PATCH 027/486] Currently, while kvm and qemu can not handle some kvm exit, qemu will do vm_stop, which will make vm in pause state. This action make vm unrecoverable, so send guest panic to libvirt instead. --- accel/kvm/kvm-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index eecd8031cf..b128d311c2 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2970,7 +2970,7 @@ int kvm_cpu_exec(CPUState *cpu) if (ret < 0) { cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); - vm_stop(RUN_STATE_INTERNAL_ERROR); + qemu_system_guest_panicked(cpu_get_crash_info(cpu)); } qatomic_set(&cpu->exit_request, 0); -- Gitee From 12706113392018fd7aa6471a3cbada62f0180539 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Wed, 9 Feb 2022 12:51:19 +0800 Subject: [PATCH 028/486] cpu/features: fix bug for memory leakage strList hash not free after used, Fix it. --- target/i386/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index aa9e636800..b9690e3250 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4752,6 +4752,7 @@ static void x86_cpu_get_unavailable_features(Object *obj, Visitor *v, x86_cpu_list_feature_names(xc->filtered_features, &result); visit_type_strList(v, "unavailable-features", &result, errp); + qapi_free_strList(result); } /* Check for missing features that may prevent the CPU class from -- Gitee From 7eb28408efe75192a0f976a197f8f1906d9073e8 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Wed, 9 Feb 2022 14:13:05 +0800 Subject: [PATCH 029/486] monitor/qmp: drop inflight rsp if qmp client broken If libvirt restart while qemu is handle qmp message, libvirt will reconnect qemu monitor socket, and query status of qemu by qmp. But qemu may return last qmp respond to new connect socket, and libvirt recv unexpected respond, So libvirt think qemu is abnormal, and will kill qemu. This patch add qmp connect id, while reconnect id will change. While respond to libvirt, judge if id is same, if not, drop this respond. --- monitor/monitor-internal.h | 1 + monitor/qmp.c | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 3da3f86c6a..5435864add 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -144,6 +144,7 @@ typedef struct { const QmpCommandList *commands; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ + uint64_t qmp_client_id; /*qmp client id, update if peer disconnect */ /* * Protects qmp request/response queue. * Take monitor_lock first when you need both. diff --git a/monitor/qmp.c b/monitor/qmp.c index 092c527b6f..4d1ac66785 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -125,18 +125,19 @@ void qmp_send_response(MonitorQMP *mon, const QDict *rsp) * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP. * Nothing is emitted then. */ -static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp) +static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp, uint64_t req_client_id) { - if (rsp) { - qmp_send_response(mon, rsp); + if (!rsp || (mon->qmp_client_id != req_client_id)) { + return; } + qmp_send_response(mon, rsp); } /* * Runs outside of coroutine context for OOB commands, but in * coroutine context for everything else. */ -static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req) +static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req, uint64_t req_client_id) { QDict *rsp; QDict *error; @@ -156,7 +157,7 @@ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req) } } - monitor_qmp_respond(mon, rsp); + monitor_qmp_respond(mon, rsp, req_client_id); qobject_unref(rsp); } @@ -315,13 +316,13 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) trace_monitor_qmp_cmd_in_band(id_json->str); g_string_free(id_json, true); } - monitor_qmp_dispatch(mon, req_obj->req); + monitor_qmp_dispatch(mon, req_obj->req, mon->qmp_client_id); } else { assert(req_obj->err); trace_monitor_qmp_err_in_band(error_get_pretty(req_obj->err)); rsp = qmp_error_response(req_obj->err); req_obj->err = NULL; - monitor_qmp_respond(mon, rsp); + monitor_qmp_respond(mon, rsp, mon->qmp_client_id); qobject_unref(rsp); } @@ -366,7 +367,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) trace_monitor_qmp_cmd_out_of_band(id_json->str); g_string_free(id_json, true); } - monitor_qmp_dispatch(mon, req); + monitor_qmp_dispatch(mon, req, mon->qmp_client_id); qobject_unref(req); return; } @@ -452,6 +453,7 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event) mon_refcount++; break; case CHR_EVENT_CLOSED: + mon->qmp_client_id++; /* * Note: this is only useful when the output of the chardev * backend is still open. For example, when the backend is @@ -505,6 +507,7 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) } qemu_chr_fe_set_echo(&mon->common.chr, true); + mon->qmp_client_id = 1; /* Note: we run QMP monitor in I/O thread when @chr supports that */ monitor_data_init(&mon->common, true, false, qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT)); -- Gitee From e5fc3a755c9ba9f18bc0416c60f745912cb5b104 Mon Sep 17 00:00:00 2001 From: Chuan Zheng Date: Wed, 9 Feb 2022 14:21:39 +0800 Subject: [PATCH 030/486] oslib-posix: optimise vm startup time for 1G hugepage It takes quit a long time to clear 1G-hugepage, which makes glibc pthread_create quit slow. Create touch_pages threads in advance, and then handle the touch_pages callback. Only read lock is held here. --- util/oslib-posix.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/util/oslib-posix.c b/util/oslib-posix.c index e8bdb02e1d..18a38b9464 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -84,6 +84,7 @@ typedef struct MemsetThread MemsetThread; static MemsetThread *memset_thread; static int memset_num_threads; +static int started_num_threads; static bool memset_thread_failed; static QemuMutex page_mutex; @@ -464,6 +465,10 @@ static void *do_touch_pages(void *arg) } qemu_mutex_unlock(&page_mutex); + while (started_num_threads != memset_num_threads) { + smp_mb(); + } + /* unblock SIGBUS */ sigemptyset(&set); sigaddset(&set, SIGBUS); @@ -529,7 +534,7 @@ static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, memset_thread = g_new0(MemsetThread, memset_num_threads); numpages_per_thread = numpages / memset_num_threads; leftover = numpages % memset_num_threads; - for (i = 0; i < memset_num_threads; i++) { + for (i = 0, started_num_threads = 0; i < memset_num_threads; i++) { memset_thread[i].addr = addr; memset_thread[i].numpages = numpages_per_thread + (i < leftover); memset_thread[i].hpagesize = hpagesize; @@ -537,6 +542,7 @@ static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, do_touch_pages, &memset_thread[i], QEMU_THREAD_JOINABLE); addr += memset_thread[i].numpages * hpagesize; + started_num_threads++; } qemu_mutex_lock(&page_mutex); -- Gitee From cae52ca5b1dd4a295eaabc9649481f3d6a684f06 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 9 Feb 2022 17:33:26 +0800 Subject: [PATCH 031/486] Revert "cpu: parse +/- feature to avoid failure" This reverts commit ef83cde8dd2c9b404527354489b14d2bd238733d. Signed-off-by: Mingwang Li --- target/arm/cpu64.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 08d886de7b..bd8e5b5676 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -983,47 +983,10 @@ static gchar *aarch64_gdb_arch_name(CPUState *cs) return g_strdup("aarch64"); } -/* Parse "+feature,-feature,feature=foo" CPU feature string - */ -static void arm_cpu_parse_featurestr(const char *typename, char *features, - Error **errp ) -{ - char *featurestr; - char *val; - static bool cpu_globals_initialized; - - if (cpu_globals_initialized) { - return; - } - cpu_globals_initialized = true; - - featurestr = features ? strtok(features, ",") : NULL; - while (featurestr) { - val = strchr(featurestr, '='); - if (val) { - GlobalProperty *prop = g_new0(typeof(*prop), 1); - *val = 0; - val++; - prop->driver = typename; - prop->property = g_strdup(featurestr); - prop->value = g_strdup(val); - qdev_prop_register_global(prop); - } else if (featurestr[0] == '+' || featurestr[0] == '-') { - warn_report("Ignore %s feature\n", featurestr); - } else { - error_setg(errp, "Expected key=value format, found %s.", - featurestr); - return; - } - featurestr = strtok(NULL, ","); - } -} - static void aarch64_cpu_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); - cc->parse_features = arm_cpu_parse_featurestr; cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; cc->gdb_num_core_regs = 34; -- Gitee From 7474971c6fd6c6f77e66ded125e5f2521c7e12a2 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 9 Feb 2022 17:35:52 +0800 Subject: [PATCH 032/486] Revert "cpu: add Cortex-A72 processor kvm target support" This reverts commit f0da7fa5230b5f771570b2c12288e4a56a20dd97. Signed-off-by: Mingwang Li --- target/arm/cpu64.c | 1 - target/arm/kvm-consts.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index bd8e5b5676..26419fe994 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -202,7 +202,6 @@ static void aarch64_a72_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a72"; - cpu->kvm_target = QEMU_KVM_ARM_TARGET_GENERIC_V8; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 5f1311ade7..580f1c1fee 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -130,8 +130,6 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED); #define QEMU_KVM_ARM_TARGET_CORTEX_A57 2 #define QEMU_KVM_ARM_TARGET_XGENE_POTENZA 3 #define QEMU_KVM_ARM_TARGET_CORTEX_A53 4 -/* Generic ARM v8 target */ -#define QEMU_KVM_ARM_TARGET_GENERIC_V8 5 /* There's no kernel define for this: sentinel value which * matches no KVM target value for either 64 or 32 bit @@ -143,7 +141,6 @@ MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53); -MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_GENERIC_V8, KVM_ARM_TARGET_GENERIC_V8); #define CP_REG_ARM64 0x6000000000000000ULL #define CP_REG_ARM_COPROC_MASK 0x000000000FFF0000 -- Gitee From 8ddc2bcb196a34cc641d50b057550e4b11dc8700 Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Wed, 9 Feb 2022 17:39:34 +0800 Subject: [PATCH 033/486] cpu: add Cortex-A72 processor kvm target support The ARM Cortex-A72 is ARMv8-A micro-architecture, add kvm target to ARM Cortex-A72 processor definition. Signed-off-by: Xu Yandong Signed-off-by: Mingwang Li --- target/arm/cpu64.c | 2 +- target/arm/kvm-consts.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 26419fe994..1b56261964 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -202,6 +202,7 @@ static void aarch64_a72_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a72"; + cpu->kvm_target = QEMU_KVM_ARM_TARGET_GENERIC_V8; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); @@ -265,7 +266,6 @@ static void aarch64_kunpeng_920_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x110305408; cpu->isar.id_aa64isar0 = 0x10211120; cpu->isar.id_aa64mmfr0 = 0x101125; - cpu->kvm_target = KVM_ARM_TARGET_GENERIC_V8; } void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 580f1c1fee..5f1311ade7 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -130,6 +130,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED); #define QEMU_KVM_ARM_TARGET_CORTEX_A57 2 #define QEMU_KVM_ARM_TARGET_XGENE_POTENZA 3 #define QEMU_KVM_ARM_TARGET_CORTEX_A53 4 +/* Generic ARM v8 target */ +#define QEMU_KVM_ARM_TARGET_GENERIC_V8 5 /* There's no kernel define for this: sentinel value which * matches no KVM target value for either 64 or 32 bit @@ -141,6 +143,7 @@ MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA); MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53); +MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_GENERIC_V8, KVM_ARM_TARGET_GENERIC_V8); #define CP_REG_ARM64 0x6000000000000000ULL #define CP_REG_ARM_COPROC_MASK 0x000000000FFF0000 -- Gitee From eee7ff398496524881225d503309a9853972c5df Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 8 Feb 2022 17:00:39 +0800 Subject: [PATCH 034/486] vfio/pci: Ascend310 need 4Bytes quirk in bar4 --- hw/vfio/pci-quirks.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 0cf69a8c6d..d86bcaf309 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1209,6 +1209,80 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, return 0; } +#define PCI_VENDOR_ID_HUAWEI 0x19e5 +#define PCI_DEVICE_ID_ASCEND310 0xd100 +#define ASCEND310_XLOADER_SIZE 4 +#define ASCEND310_XLOADER_OFFSET 0x400 + +typedef struct VFIOAscendBarQuirk { + struct VFIOPCIDevice *vdev; + pcibus_t offset; + uint8_t bar; + MemoryRegion *mem; +} VFIOAscendBarQuirk; + +static uint64_t vfio_ascend_quirk_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOAscendBarQuirk *quirk = opaque; + VFIOPCIDevice *vdev = quirk->vdev; + + qemu_log("read RO region! addr=0x%" HWADDR_PRIx ", size=%d\n", + addr + quirk->offset, size); + + return vfio_region_read(&vdev->bars[quirk->bar].region, + addr + quirk->offset, size); +} + +static void vfio_ascend_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOAscendBarQuirk *quirk = opaque; + + qemu_log("modifying RO region is not allowed! addr=0x%" + HWADDR_PRIx ", data=0x%" PRIx64 ", size=%d\n", + addr + quirk->offset, data, size); +} + +static const MemoryRegionOps vfio_ascend_intercept_regs_quirk = { + .read = vfio_ascend_quirk_read, + .write = vfio_ascend_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_ascend310_bar4_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOAscendBarQuirk *bar4_quirk; + + if (vdev->vendor_id != PCI_VENDOR_ID_HUAWEI || nr != 4 || + vdev->device_id != PCI_DEVICE_ID_ASCEND310) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->nr_mem = 1; + quirk->mem = g_new0(MemoryRegion, quirk->nr_mem); + bar4_quirk = quirk->data = g_new0(typeof(*bar4_quirk), quirk->nr_mem); + bar4_quirk[0].vdev = vdev; + bar4_quirk[0].offset = ASCEND310_XLOADER_OFFSET; + bar4_quirk[0].bar = nr; + + /* + * intercept w/r to the xloader-updating register, + * so the vm can't enable xloader-updating + */ + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), + &vfio_ascend_intercept_regs_quirk, + &bar4_quirk[0], + "vfio-ascend310-bar4-intercept-regs-quirk", + ASCEND310_XLOADER_SIZE); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + bar4_quirk[0].offset, + &quirk->mem[0], 1); + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); +} + /* * Common quirk probe entry points. */ @@ -1261,6 +1335,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) #ifdef CONFIG_VFIO_IGD vfio_probe_igd_bar4_quirk(vdev, nr); #endif + vfio_probe_ascend310_bar4_quirk(vdev, nr); } void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) -- Gitee From bcc63ff3975cca783308fac7517a7911c29bd7c1 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 8 Feb 2022 17:16:04 +0800 Subject: [PATCH 035/486] vfio/pci: Ascend710 need 4Bytes quirk in bar0 --- hw/vfio/pci-quirks.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index d86bcaf309..6a9fc0afc5 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1210,7 +1210,10 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, } #define PCI_VENDOR_ID_HUAWEI 0x19e5 +#define PCI_DEVICE_ID_ASCEND710 0xd500 #define PCI_DEVICE_ID_ASCEND310 0xd100 +#define ASCEND710_XLOADER_SIZE 4 +#define ASCEND710_XLOADER_OFFSET 0x20430 #define ASCEND310_XLOADER_SIZE 4 #define ASCEND310_XLOADER_OFFSET 0x400 @@ -1250,6 +1253,39 @@ static const MemoryRegionOps vfio_ascend_intercept_regs_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void vfio_probe_ascend710_bar0_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOAscendBarQuirk *bar0_quirk; + + if (vdev->vendor_id != PCI_VENDOR_ID_HUAWEI || nr != 0 || + vdev->device_id != PCI_DEVICE_ID_ASCEND710) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->nr_mem = 1; + quirk->mem = g_new0(MemoryRegion, quirk->nr_mem); + bar0_quirk = quirk->data = g_new0(typeof(*bar0_quirk), quirk->nr_mem); + bar0_quirk[0].vdev = vdev; + bar0_quirk[0].offset = ASCEND710_XLOADER_OFFSET; + bar0_quirk[0].bar = nr; + + /* + * intercept w/r to the xloader-updating register, + * so the vm can't enable xloader-updating + */ + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), + &vfio_ascend_intercept_regs_quirk, + &bar0_quirk[0], + "vfio-ascend710-bar0-intercept-regs-quirk", + ASCEND710_XLOADER_SIZE); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + bar0_quirk[0].offset, + &quirk->mem[0], 1); + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); +} + static void vfio_probe_ascend310_bar4_quirk(VFIOPCIDevice *vdev, int nr) { VFIOQuirk *quirk; @@ -1335,6 +1371,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) #ifdef CONFIG_VFIO_IGD vfio_probe_igd_bar4_quirk(vdev, nr); #endif + vfio_probe_ascend710_bar0_quirk(vdev, nr); vfio_probe_ascend310_bar4_quirk(vdev, nr); } -- Gitee From 4cf7d00e43c4e90327e13afa3cbc9c7ca3657c9f Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 8 Feb 2022 19:20:36 +0800 Subject: [PATCH 036/486] vfio/pci: Ascend910 need 4Bytes quirk in bar0 --- hw/vfio/pci-quirks.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 6a9fc0afc5..2457a61196 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1210,8 +1210,11 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, } #define PCI_VENDOR_ID_HUAWEI 0x19e5 +#define PCI_DEVICE_ID_ASCEND910 0xd801 #define PCI_DEVICE_ID_ASCEND710 0xd500 #define PCI_DEVICE_ID_ASCEND310 0xd100 +#define ASCEND910_XLOADER_SIZE 4 +#define ASCEND910_XLOADER_OFFSET 0x80400 #define ASCEND710_XLOADER_SIZE 4 #define ASCEND710_XLOADER_OFFSET 0x20430 #define ASCEND310_XLOADER_SIZE 4 @@ -1253,6 +1256,39 @@ static const MemoryRegionOps vfio_ascend_intercept_regs_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void vfio_probe_ascend910_bar0_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + VFIOAscendBarQuirk *bar0_quirk; + + if (vdev->vendor_id != PCI_VENDOR_ID_HUAWEI || nr != 0 || + vdev->device_id != PCI_DEVICE_ID_ASCEND910) { + return; + } + + quirk = g_malloc0(sizeof(*quirk)); + quirk->nr_mem = 1; + quirk->mem = g_new0(MemoryRegion, quirk->nr_mem); + bar0_quirk = quirk->data = g_new0(typeof(*bar0_quirk), quirk->nr_mem); + bar0_quirk[0].vdev = vdev; + bar0_quirk[0].offset = ASCEND910_XLOADER_OFFSET; + bar0_quirk[0].bar = nr; + + /* + * intercept w/r to the xloader-updating register, + * so the vm can't enable xloader-updating + */ + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), + &vfio_ascend_intercept_regs_quirk, + &bar0_quirk[0], + "vfio-ascend910-bar0-intercept-regs-quirk", + ASCEND910_XLOADER_SIZE); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + bar0_quirk[0].offset, + &quirk->mem[0], 1); + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); +} + static void vfio_probe_ascend710_bar0_quirk(VFIOPCIDevice *vdev, int nr) { VFIOQuirk *quirk; @@ -1371,6 +1407,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) #ifdef CONFIG_VFIO_IGD vfio_probe_igd_bar4_quirk(vdev, nr); #endif + vfio_probe_ascend910_bar0_quirk(vdev, nr); vfio_probe_ascend710_bar0_quirk(vdev, nr); vfio_probe_ascend310_bar4_quirk(vdev, nr); } -- Gitee From b04e92ed13e49f666f62c8f3daa5746109caf17b Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Mon, 22 Nov 2021 11:26:51 +0800 Subject: [PATCH 037/486] qapi/machine.json: Fix incorrect description for die-id In terms of scope, die-id should mean "the die number within socket the CPU belongs to" instead of "the die number within node/board the CPU belongs to". Fix it to avoid confusing the Doc reader. Fixes: 176d2cda0d ("i386/cpu: Consolidate die-id validity in smp context") Signed-off-by: Yanan Wang Reviewed-by: Eric Blake Message-Id: <20211122032651.16064-1-wangyanan55@huawei.com> Signed-off-by: Paolo Bonzini --- qapi/machine.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/machine.json b/qapi/machine.json index 067e3f5378..f1839acf20 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -867,7 +867,7 @@ # # @node-id: NUMA node ID the CPU belongs to # @socket-id: socket number within node/board the CPU belongs to -# @die-id: die number within node/board the CPU belongs to (Since 4.1) +# @die-id: die number within socket the CPU belongs to (since 4.1) # @core-id: core number within die the CPU belongs to # @thread-id: thread number within core the CPU belongs to # -- Gitee From d8b2aee4fd6ccd8eb621522b647c392c1dd7955c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Nov 2021 12:32:09 +0100 Subject: [PATCH 038/486] tests/unit/test-smp-parse: Pass machine type as argument to tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use g_test_add_data_func() instead of g_test_add_func() so we can pass the machine type to the tests (we will soon have different machine types). Reviewed-by: Richard Henderson Reviewed-by: Yanan Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211216132015.815493-2-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index b02450e25a..37c6b4981d 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -487,9 +487,10 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } -static void test_generic(void) +static void test_generic(const void *opaque) { - Object *obj = object_new(TYPE_MACHINE); + const char *machine_type = opaque; + Object *obj = object_new(machine_type); MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); SMPTestData *data = &(SMPTestData){{ }}; @@ -525,9 +526,10 @@ static void test_generic(void) object_unref(obj); } -static void test_with_dies(void) +static void test_with_dies(const void *opaque) { - Object *obj = object_new(TYPE_MACHINE); + const char *machine_type = opaque; + Object *obj = object_new(machine_type); MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); SMPTestData *data = &(SMPTestData){{ }}; @@ -599,8 +601,12 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); - g_test_add_func("/test-smp-parse/generic", test_generic); - g_test_add_func("/test-smp-parse/with_dies", test_with_dies); + g_test_add_data_func("/test-smp-parse/generic", + TYPE_MACHINE, + test_generic); + g_test_add_data_func("/test-smp-parse/with_dies", + TYPE_MACHINE, + test_with_dies); g_test_run(); -- Gitee From fad259cf9996dbc4001cb94ec3c846d649401027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Nov 2021 12:35:43 +0100 Subject: [PATCH 039/486] tests/unit/test-smp-parse: Split the 'generic' test in valid / invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the 'generic' test in two tests: 'valid' and 'invalid'. This will allow us to remove the hack which modifies the MachineClass internal state. Reviewed-by: Richard Henderson Reviewed-by: Yanan Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211216132015.815493-3-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 37c6b4981d..425ed6b6b9 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -487,7 +487,7 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } -static void test_generic(const void *opaque) +static void test_generic_valid(const void *opaque) { const char *machine_type = opaque; Object *obj = object_new(machine_type); @@ -508,6 +508,18 @@ static void test_generic(const void *opaque) smp_parse_test(ms, data, true); } + object_unref(obj); +} + +static void test_generic_invalid(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData *data = &(SMPTestData){}; + int i; + /* Force invalid min CPUs and max CPUs */ mc->min_cpus = 2; mc->max_cpus = 511; @@ -601,9 +613,12 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); - g_test_add_data_func("/test-smp-parse/generic", + g_test_add_data_func("/test-smp-parse/generic/valid", + TYPE_MACHINE, + test_generic_valid); + g_test_add_data_func("/test-smp-parse/generic/invalid", TYPE_MACHINE, - test_generic); + test_generic_invalid); g_test_add_data_func("/test-smp-parse/with_dies", TYPE_MACHINE, test_with_dies); -- Gitee From 4981e75623db6ca681d13719ffcf61b0cfac3edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Nov 2021 12:39:12 +0100 Subject: [PATCH 040/486] tests/unit/test-smp-parse: Add 'smp-with-dies' machine type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid modifying the MachineClass internals by adding the 'smp-with-dies' machine, which inherits from TYPE_MACHINE. Reviewed-by: Richard Henderson Reviewed-by: Yanan Wang Tested-by: Yanan Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211216132015.815493-4-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 425ed6b6b9..f66cf7bb59 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -487,6 +487,16 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } +static void machine_with_dies_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->min_cpus = MIN_CPUS; + mc->max_cpus = MAX_CPUS; + + mc->smp_props.dies_supported = true; +} + static void test_generic_valid(const void *opaque) { const char *machine_type = opaque; @@ -548,9 +558,6 @@ static void test_with_dies(const void *opaque) unsigned int num_dies = 2; int i; - /* Force the SMP compat properties */ - mc->smp_props.dies_supported = true; - for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { *data = data_generic_valid[i]; unsupported_params_init(mc, data); @@ -588,9 +595,6 @@ static void test_with_dies(const void *opaque) smp_parse_test(ms, data, false); } - /* Restore the SMP compat properties */ - mc->smp_props.dies_supported = false; - object_unref(obj); } @@ -602,6 +606,10 @@ static const TypeInfo smp_machine_types[] = { .class_init = machine_base_class_init, .class_size = sizeof(MachineClass), .instance_size = sizeof(MachineState), + }, { + .name = MACHINE_TYPE_NAME("smp-with-dies"), + .parent = TYPE_MACHINE, + .class_init = machine_with_dies_class_init, } }; @@ -620,7 +628,7 @@ int main(int argc, char *argv[]) TYPE_MACHINE, test_generic_invalid); g_test_add_data_func("/test-smp-parse/with_dies", - TYPE_MACHINE, + MACHINE_TYPE_NAME("smp-with-dies"), test_with_dies); g_test_run(); -- Gitee From 9a98659dcb37c81e69f54d8f6cbe5116ceba5a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Nov 2021 15:44:07 +0100 Subject: [PATCH 041/486] tests/unit/test-smp-parse: Add 'smp-generic-invalid' machine type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid modifying the MachineClass internals by adding the 'smp-generic-invalid' machine, which inherits from TYPE_MACHINE. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Message-Id: <20211216132015.815493-5-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index f66cf7bb59..47e11089e2 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -487,6 +487,17 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } +static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + /* Force invalid min CPUs and max CPUs */ + mc->min_cpus = 2; + mc->max_cpus = 511; + + mc->smp_props.dies_supported = false; +} + static void machine_with_dies_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -530,10 +541,6 @@ static void test_generic_invalid(const void *opaque) SMPTestData *data = &(SMPTestData){}; int i; - /* Force invalid min CPUs and max CPUs */ - mc->min_cpus = 2; - mc->max_cpus = 511; - for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) { *data = data_generic_invalid[i]; unsupported_params_init(mc, data); @@ -541,10 +548,6 @@ static void test_generic_invalid(const void *opaque) smp_parse_test(ms, data, false); } - /* Reset the supported min CPUs and max CPUs */ - mc->min_cpus = MIN_CPUS; - mc->max_cpus = MAX_CPUS; - object_unref(obj); } @@ -606,6 +609,10 @@ static const TypeInfo smp_machine_types[] = { .class_init = machine_base_class_init, .class_size = sizeof(MachineClass), .instance_size = sizeof(MachineState), + }, { + .name = MACHINE_TYPE_NAME("smp-generic-invalid"), + .parent = TYPE_MACHINE, + .class_init = machine_generic_invalid_class_init, }, { .name = MACHINE_TYPE_NAME("smp-with-dies"), .parent = TYPE_MACHINE, @@ -625,7 +632,7 @@ int main(int argc, char *argv[]) TYPE_MACHINE, test_generic_valid); g_test_add_data_func("/test-smp-parse/generic/invalid", - TYPE_MACHINE, + MACHINE_TYPE_NAME("smp-generic-invalid"), test_generic_invalid); g_test_add_data_func("/test-smp-parse/with_dies", MACHINE_TYPE_NAME("smp-with-dies"), -- Gitee From c33c7dd51eebf5ae7b7ece1e829b0a5ffdcebfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 15 Nov 2021 15:49:59 +0100 Subject: [PATCH 042/486] tests/unit/test-smp-parse: Add 'smp-generic-valid' machine type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the common TYPE_MACHINE class initialization in machine_base_class_init(), make it abstract, and move the non-common code to a new class: "smp-generic-valid". Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yanan Wang Message-Id: <20211216132015.815493-6-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 47e11089e2..b20bf2c235 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -478,13 +478,19 @@ static void machine_base_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + mc->smp_props.prefer_sockets = true; + + mc->name = g_strdup(SMP_MACHINE_NAME); +} + +static void machine_generic_valid_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + mc->min_cpus = MIN_CPUS; mc->max_cpus = MAX_CPUS; - mc->smp_props.prefer_sockets = true; mc->smp_props.dies_supported = false; - - mc->name = g_strdup(SMP_MACHINE_NAME); } static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) @@ -606,9 +612,14 @@ static const TypeInfo smp_machine_types[] = { { .name = TYPE_MACHINE, .parent = TYPE_OBJECT, + .abstract = true, .class_init = machine_base_class_init, .class_size = sizeof(MachineClass), .instance_size = sizeof(MachineState), + }, { + .name = MACHINE_TYPE_NAME("smp-generic-valid"), + .parent = TYPE_MACHINE, + .class_init = machine_generic_valid_class_init, }, { .name = MACHINE_TYPE_NAME("smp-generic-invalid"), .parent = TYPE_MACHINE, @@ -629,7 +640,7 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); g_test_add_data_func("/test-smp-parse/generic/valid", - TYPE_MACHINE, + MACHINE_TYPE_NAME("smp-generic-valid"), test_generic_valid); g_test_add_data_func("/test-smp-parse/generic/invalid", MACHINE_TYPE_NAME("smp-generic-invalid"), -- Gitee From 964965721bbed1941bf77e5a748efc1274b7c289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Nov 2021 08:58:40 +0100 Subject: [PATCH 043/486] tests/unit/test-smp-parse: Simplify pointer to compound literal use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can simply use a local variable (and pass its pointer) instead of a pointer to a compound literal. Reviewed-by: Andrew Jones Reviewed-by: Richard Henderson Reviewed-by: Yanan Wang Tested-by: Yanan Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211216132015.815493-7-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 66 ++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index b20bf2c235..395929b66c 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -520,19 +520,19 @@ static void test_generic_valid(const void *opaque) Object *obj = object_new(machine_type); MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - SMPTestData *data = &(SMPTestData){{ }}; + SMPTestData data = {}; int i; for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { - *data = data_generic_valid[i]; - unsupported_params_init(mc, data); + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); - smp_parse_test(ms, data, true); + smp_parse_test(ms, &data, true); /* Unsupported parameters can be provided with their values as 1 */ - data->config.has_dies = true; - data->config.dies = 1; - smp_parse_test(ms, data, true); + data.config.has_dies = true; + data.config.dies = 1; + smp_parse_test(ms, &data, true); } object_unref(obj); @@ -544,14 +544,14 @@ static void test_generic_invalid(const void *opaque) Object *obj = object_new(machine_type); MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - SMPTestData *data = &(SMPTestData){}; + SMPTestData data = {}; int i; for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) { - *data = data_generic_invalid[i]; - unsupported_params_init(mc, data); + data = data_generic_invalid[i]; + unsupported_params_init(mc, &data); - smp_parse_test(ms, data, false); + smp_parse_test(ms, &data, false); } object_unref(obj); @@ -563,45 +563,45 @@ static void test_with_dies(const void *opaque) Object *obj = object_new(machine_type); MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - SMPTestData *data = &(SMPTestData){{ }}; + SMPTestData data = {}; unsigned int num_dies = 2; int i; for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { - *data = data_generic_valid[i]; - unsupported_params_init(mc, data); + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); /* when dies parameter is omitted, it will be set as 1 */ - data->expect_prefer_sockets.dies = 1; - data->expect_prefer_cores.dies = 1; + data.expect_prefer_sockets.dies = 1; + data.expect_prefer_cores.dies = 1; - smp_parse_test(ms, data, true); + smp_parse_test(ms, &data, true); /* when dies parameter is specified */ - data->config.has_dies = true; - data->config.dies = num_dies; - if (data->config.has_cpus) { - data->config.cpus *= num_dies; + data.config.has_dies = true; + data.config.dies = num_dies; + if (data.config.has_cpus) { + data.config.cpus *= num_dies; } - if (data->config.has_maxcpus) { - data->config.maxcpus *= num_dies; + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_dies; } - data->expect_prefer_sockets.dies = num_dies; - data->expect_prefer_sockets.cpus *= num_dies; - data->expect_prefer_sockets.max_cpus *= num_dies; - data->expect_prefer_cores.dies = num_dies; - data->expect_prefer_cores.cpus *= num_dies; - data->expect_prefer_cores.max_cpus *= num_dies; + data.expect_prefer_sockets.dies = num_dies; + data.expect_prefer_sockets.cpus *= num_dies; + data.expect_prefer_sockets.max_cpus *= num_dies; + data.expect_prefer_cores.dies = num_dies; + data.expect_prefer_cores.cpus *= num_dies; + data.expect_prefer_cores.max_cpus *= num_dies; - smp_parse_test(ms, data, true); + smp_parse_test(ms, &data, true); } for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) { - *data = data_with_dies_invalid[i]; - unsupported_params_init(mc, data); + data = data_with_dies_invalid[i]; + unsupported_params_init(mc, &data); - smp_parse_test(ms, data, false); + smp_parse_test(ms, &data, false); } object_unref(obj); -- Gitee From bcf4b802bd8971c0c5a255e606b15900cd47c6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Nov 2021 10:23:06 +0100 Subject: [PATCH 044/486] tests/unit/test-smp-parse: Constify some pointer/struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare structures const when we don't need to modify them at runtime. Reviewed-by: Andrew Jones Reviewed-by: Richard Henderson Reviewed-by: Yanan Wang Tested-by: Yanan Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211216132015.815493-8-philmd@redhat.com> --- tests/unit/test-smp-parse.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 395929b66c..0f98c9509e 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -83,7 +83,7 @@ typedef struct SMPTestData { * then test the automatic calculation algorithm of the missing * values in the parser. */ -static struct SMPTestData data_generic_valid[] = { +static const struct SMPTestData data_generic_valid[] = { { /* config: no configuration provided * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */ @@ -285,7 +285,7 @@ static struct SMPTestData data_generic_valid[] = { }, }; -static struct SMPTestData data_generic_invalid[] = { +static const struct SMPTestData data_generic_invalid[] = { { /* config: -smp 2,dies=2 */ .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), @@ -319,7 +319,7 @@ static struct SMPTestData data_generic_invalid[] = { }, }; -static struct SMPTestData data_with_dies_invalid[] = { +static const struct SMPTestData data_with_dies_invalid[] = { { /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */ .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16), @@ -356,7 +356,7 @@ static char *smp_config_to_string(SMPConfiguration *config) config->has_maxcpus ? "true" : "false", config->maxcpus); } -static char *cpu_topology_to_string(CpuTopology *topo) +static char *cpu_topology_to_string(const CpuTopology *topo) { return g_strdup_printf( "(CpuTopology) {\n" @@ -372,7 +372,7 @@ static char *cpu_topology_to_string(CpuTopology *topo) } static void check_parse(MachineState *ms, SMPConfiguration *config, - CpuTopology *expect_topo, const char *expect_err, + const CpuTopology *expect_topo, const char *expect_err, bool is_valid) { g_autofree char *config_str = smp_config_to_string(config); @@ -466,7 +466,7 @@ static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid) } /* The parsed results of the unsupported parameters should be 1 */ -static void unsupported_params_init(MachineClass *mc, SMPTestData *data) +static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) { if (!mc->smp_props.dies_supported) { data->expect_prefer_sockets.dies = 1; -- Gitee From 2ce1daae407033e689a559b7346523b18651ee0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Nov 2021 10:21:23 +0100 Subject: [PATCH 045/486] hw/core: Rename smp_parse() -> machine_parse_smp_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All methods related to MachineState are prefixed with "machine_". smp_parse() does not need to be an exception. Rename it and const'ify the SMPConfiguration argument, since it doesn't need to be modified. Reviewed-by: Andrew Jones Reviewed-by: Richard Henderson Reviewed-by: Yanan Wang Tested-by: Yanan Wang Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20211216132015.815493-9-philmd@redhat.com> --- hw/core/machine-smp.c | 6 ++++-- hw/core/machine.c | 2 +- include/hw/boards.h | 3 ++- tests/unit/test-smp-parse.c | 8 ++++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 116a0cbbfa..2cbfd57429 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -44,7 +44,8 @@ static char *cpu_hierarchy_to_string(MachineState *ms) } /* - * smp_parse - Generic function used to parse the given SMP configuration + * machine_parse_smp_config: Generic function used to parse the given + * SMP configuration * * Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be * automatically computed based on the provided ones. @@ -63,7 +64,8 @@ static char *cpu_hierarchy_to_string(MachineState *ms) * introduced topology members which are likely to be target specific should * be directly set as 1 if they are omitted (e.g. dies for PC since 4.1). */ -void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp) +void machine_parse_smp_config(MachineState *ms, + const SMPConfiguration *config, Error **errp) { MachineClass *mc = MACHINE_GET_CLASS(ms); unsigned cpus = config->has_cpus ? config->cpus : 0; diff --git a/hw/core/machine.c b/hw/core/machine.c index 53a99abc56..3993c534b9 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -761,7 +761,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, return; } - smp_parse(ms, config, errp); + machine_parse_smp_config(ms, config, errp); } static void machine_class_init(ObjectClass *oc, void *data) diff --git a/include/hw/boards.h b/include/hw/boards.h index 9c1c190104..7597cec440 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -34,7 +34,8 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine); void machine_set_cpu_numa_node(MachineState *machine, const CpuInstanceProperties *props, Error **errp); -void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp); +void machine_parse_smp_config(MachineState *ms, + const SMPConfiguration *config, Error **errp); /** * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 0f98c9509e..b6df8137fc 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -337,7 +337,7 @@ static const struct SMPTestData data_with_dies_invalid[] = { }, }; -static char *smp_config_to_string(SMPConfiguration *config) +static char *smp_config_to_string(const SMPConfiguration *config) { return g_strdup_printf( "(SMPConfiguration) {\n" @@ -371,7 +371,7 @@ static char *cpu_topology_to_string(const CpuTopology *topo) topo->cores, topo->threads, topo->max_cpus); } -static void check_parse(MachineState *ms, SMPConfiguration *config, +static void check_parse(MachineState *ms, const SMPConfiguration *config, const CpuTopology *expect_topo, const char *expect_err, bool is_valid) { @@ -380,8 +380,8 @@ static void check_parse(MachineState *ms, SMPConfiguration *config, g_autofree char *output_topo_str = NULL; Error *err = NULL; - /* call the generic parser smp_parse() */ - smp_parse(ms, config, &err); + /* call the generic parser */ + machine_parse_smp_config(ms, config, &err); output_topo_str = cpu_topology_to_string(&ms->smp); -- Gitee From 07991b049fc9ebdb62c311eda1535ad4831625e5 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Dec 2021 17:22:08 +0800 Subject: [PATCH 046/486] qemu-options: Improve readability of SMP related Docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a description in qemu-options.hx for each CPU topology parameter to explain what it exactly means, and also an extra declaration for the target-specific one, e.g. "for PC only" when describing "dies", and "for PC, it's on one die" when describing "cores". Now we are going to introduce one more non-generic parameter "clusters", it will make the Doc less readable and if we still continue to use the legacy way to describe it. So let's at first make two tweaks of the Docs to improve the readability and also scalability: 1) In the -help text: Delete the extra specific declaration and describe each topology parameter level by level. Then add a note to declare that different machines may support different subsets and the actual meaning of the supported parameters will vary accordingly. 2) In the rST text: List all the sub-hierarchies currently supported in QEMU, and correspondingly give an example of -smp configuration for each of them. Signed-off-by: Yanan Wang Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211228092221.21068-2-wangyanan55@huawei.com> Signed-off-by: Philippe Mathieu-Daudé --- qemu-options.hx | 76 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index ae2c6dbbfc..7a59db7764 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -207,14 +207,26 @@ ERST DEF("smp", HAS_ARG, QEMU_OPTION_smp, "-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]\n" - " set the number of CPUs to 'n' [default=1]\n" + " set the number of initial CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total CPUs, including\n" " offline CPUs for hotplug, etc\n" - " sockets= number of discrete sockets in the system\n" - " dies= number of CPU dies on one socket (for PC only)\n" - " cores= number of CPU cores on one socket (for PC, it's on one die)\n" - " threads= number of threads on one CPU core\n", - QEMU_ARCH_ALL) + " sockets= number of sockets on the machine board\n" + " dies= number of dies in one socket\n" + " cores= number of cores in one die\n" + " threads= number of threads in one core\n" + "Note: Different machines may have different subsets of the CPU topology\n" + " parameters supported, so the actual meaning of the supported parameters\n" + " will vary accordingly. For example, for a machine type that supports a\n" + " three-level CPU hierarchy of sockets/cores/threads, the parameters will\n" + " sequentially mean as below:\n" + " sockets means the number of sockets on the machine board\n" + " cores means the number of cores in one socket\n" + " threads means the number of threads in one core\n" + " For a particular machine type board, an expected CPU topology hierarchy\n" + " can be defined through the supported sub-option. Unsupported parameters\n" + " can also be provided in addition to the sub-option, but their values\n" + " must be set as 1 in the purpose of correct parsing.\n", + QEMU_ARCH_ALL) SRST ``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]`` Simulate a SMP system with '\ ``n``\ ' CPUs initially present on @@ -225,27 +237,57 @@ SRST initial CPU count will match the maximum number. When only one of them is given then the omitted one will be set to its counterpart's value. Both parameters may be specified, but the maximum number of CPUs must - be equal to or greater than the initial CPU count. Both parameters are - subject to an upper limit that is determined by the specific machine - type chosen. - - To control reporting of CPU topology information, the number of sockets, - dies per socket, cores per die, and threads per core can be specified. - The sum `` sockets * cores * dies * threads `` must be equal to the - maximum CPU count. CPU targets may only support a subset of the topology - parameters. Where a CPU target does not support use of a particular - topology parameter, its value should be assumed to be 1 for the purpose - of computing the CPU maximum count. + be equal to or greater than the initial CPU count. Product of the + CPU topology hierarchy must be equal to the maximum number of CPUs. + Both parameters are subject to an upper limit that is determined by + the specific machine type chosen. + + To control reporting of CPU topology information, values of the topology + parameters can be specified. Machines may only support a subset of the + parameters and different machines may have different subsets supported + which vary depending on capacity of the corresponding CPU targets. So + for a particular machine type board, an expected topology hierarchy can + be defined through the supported sub-option. Unsupported parameters can + also be provided in addition to the sub-option, but their values must be + set as 1 in the purpose of correct parsing. Either the initial CPU count, or at least one of the topology parameters must be specified. The specified parameters must be greater than zero, explicit configuration like "cpus=0" is not allowed. Values for any omitted parameters will be computed from those which are given. + + For example, the following sub-option defines a CPU topology hierarchy + (2 sockets totally on the machine, 2 cores per socket, 2 threads per + core) for a machine that only supports sockets/cores/threads. + Some members of the option can be omitted but their values will be + automatically computed: + + :: + + -smp 8,sockets=2,cores=2,threads=2,maxcpus=8 + + The following sub-option defines a CPU topology hierarchy (2 sockets + totally on the machine, 2 dies per socket, 2 cores per die, 2 threads + per core) for PC machines which support sockets/dies/cores/threads. + Some members of the option can be omitted but their values will be + automatically computed: + + :: + + -smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 + Historically preference was given to the coarsest topology parameters when computing missing values (ie sockets preferred over cores, which were preferred over threads), however, this behaviour is considered liable to change. Prior to 6.2 the preference was sockets over cores over threads. Since 6.2 the preference is cores over sockets over threads. + + For example, the following option defines a machine board with 2 sockets + of 1 core before 6.2 and 1 socket of 2 cores after 6.2: + + :: + + -smp 2 ERST DEF("numa", HAS_ARG, QEMU_OPTION_numa, -- Gitee From bf4a20a82bd4804842dd2960db30e0be7ecb2d32 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Dec 2021 17:22:09 +0800 Subject: [PATCH 047/486] hw/core/machine: Introduce CPU cluster topology support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new Cluster-Aware Scheduling support has landed in Linux 5.16, which has been proved to benefit the scheduling performance (e.g. load balance and wake_affine strategy) on both x86_64 and AArch64. So now in Linux 5.16 we have four-level arch-neutral CPU topology definition like below and a new scheduler level for clusters. struct cpu_topology { int thread_id; int core_id; int cluster_id; int package_id; int llc_id; cpumask_t thread_sibling; cpumask_t core_sibling; cpumask_t cluster_sibling; cpumask_t llc_sibling; } A cluster generally means a group of CPU cores which share L2 cache or other mid-level resources, and it is the shared resources that is used to improve scheduler's behavior. From the point of view of the size range, it's between CPU die and CPU core. For example, on some ARM64 Kunpeng servers, we have 6 clusters in each NUMA node, and 4 CPU cores in each cluster. The 4 CPU cores share a separate L2 cache and a L3 cache tag, which brings cache affinity advantage. In virtualization, on the Hosts which have pClusters (physical clusters), if we can design a vCPU topology with cluster level for guest kernel and have a dedicated vCPU pinning. A Cluster-Aware Guest kernel can also make use of the cache affinity of CPU clusters to gain similar scheduling performance. This patch adds infrastructure for CPU cluster level topology configuration and parsing, so that the user can specify cluster parameter if their machines support it. Signed-off-by: Yanan Wang Message-Id: <20211228092221.21068-3-wangyanan55@huawei.com> Reviewed-by: Philippe Mathieu-Daudé [PMD: Added '(since 7.0)' to @clusters in qapi/machine.json] Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-smp.c | 26 +++++++++++++++++++------- hw/core/machine.c | 3 +++ include/hw/boards.h | 6 +++++- qapi/machine.json | 5 ++++- qemu-options.hx | 7 ++++--- softmmu/vl.c | 3 +++ 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 2cbfd57429..b39ed21e65 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -37,6 +37,10 @@ static char *cpu_hierarchy_to_string(MachineState *ms) g_string_append_printf(s, " * dies (%u)", ms->smp.dies); } + if (mc->smp_props.clusters_supported) { + g_string_append_printf(s, " * clusters (%u)", ms->smp.clusters); + } + g_string_append_printf(s, " * cores (%u)", ms->smp.cores); g_string_append_printf(s, " * threads (%u)", ms->smp.threads); @@ -71,6 +75,7 @@ void machine_parse_smp_config(MachineState *ms, unsigned cpus = config->has_cpus ? config->cpus : 0; unsigned sockets = config->has_sockets ? config->sockets : 0; unsigned dies = config->has_dies ? config->dies : 0; + unsigned clusters = config->has_clusters ? config->clusters : 0; unsigned cores = config->has_cores ? config->cores : 0; unsigned threads = config->has_threads ? config->threads : 0; unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; @@ -82,6 +87,7 @@ void machine_parse_smp_config(MachineState *ms, if ((config->has_cpus && config->cpus == 0) || (config->has_sockets && config->sockets == 0) || (config->has_dies && config->dies == 0) || + (config->has_clusters && config->clusters == 0) || (config->has_cores && config->cores == 0) || (config->has_threads && config->threads == 0) || (config->has_maxcpus && config->maxcpus == 0)) { @@ -97,8 +103,13 @@ void machine_parse_smp_config(MachineState *ms, error_setg(errp, "dies not supported by this machine's CPU topology"); return; } + if (!mc->smp_props.clusters_supported && clusters > 1) { + error_setg(errp, "clusters not supported by this machine's CPU topology"); + return; + } dies = dies > 0 ? dies : 1; + clusters = clusters > 0 ? clusters : 1; /* compute missing values based on the provided ones */ if (cpus == 0 && maxcpus == 0) { @@ -113,41 +124,42 @@ void machine_parse_smp_config(MachineState *ms, if (sockets == 0) { cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); + sockets = maxcpus / (dies * clusters * cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); + cores = maxcpus / (sockets * dies * clusters * threads); } } else { /* prefer cores over sockets since 6.2 */ if (cores == 0) { sockets = sockets > 0 ? sockets : 1; threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * threads); + cores = maxcpus / (sockets * dies * clusters * threads); } else if (sockets == 0) { threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * cores * threads); + sockets = maxcpus / (dies * clusters * cores * threads); } } /* try to calculate omitted threads at last */ if (threads == 0) { - threads = maxcpus / (sockets * dies * cores); + threads = maxcpus / (sockets * dies * clusters * cores); } } - maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads; + maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * clusters * cores * threads; cpus = cpus > 0 ? cpus : maxcpus; ms->smp.cpus = cpus; ms->smp.sockets = sockets; ms->smp.dies = dies; + ms->smp.clusters = clusters; ms->smp.cores = cores; ms->smp.threads = threads; ms->smp.max_cpus = maxcpus; /* sanity-check of the computed topology */ - if (sockets * dies * cores * threads != maxcpus) { + if (sockets * dies * clusters * cores * threads != maxcpus) { g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); error_setg(errp, "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " diff --git a/hw/core/machine.c b/hw/core/machine.c index 3993c534b9..a4a2df405f 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -742,10 +742,12 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name, .has_cpus = true, .cpus = ms->smp.cpus, .has_sockets = true, .sockets = ms->smp.sockets, .has_dies = true, .dies = ms->smp.dies, + .has_clusters = true, .clusters = ms->smp.clusters, .has_cores = true, .cores = ms->smp.cores, .has_threads = true, .threads = ms->smp.threads, .has_maxcpus = true, .maxcpus = ms->smp.max_cpus, }; + if (!visit_type_SMPConfiguration(v, name, &config, &error_abort)) { return; } @@ -932,6 +934,7 @@ static void machine_initfn(Object *obj) ms->smp.max_cpus = mc->default_cpus; ms->smp.sockets = 1; ms->smp.dies = 1; + ms->smp.clusters = 1; ms->smp.cores = 1; ms->smp.threads = 1; } diff --git a/include/hw/boards.h b/include/hw/boards.h index 7597cec440..f49a2578ea 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -129,10 +129,12 @@ typedef struct { * SMPCompatProps: * @prefer_sockets - whether sockets are preferred over cores in smp parsing * @dies_supported - whether dies are supported by the machine + * @clusters_supported - whether clusters are supported by the machine */ typedef struct { bool prefer_sockets; bool dies_supported; + bool clusters_supported; } SMPCompatProps; /** @@ -299,7 +301,8 @@ typedef struct DeviceMemoryState { * @cpus: the number of present logical processors on the machine * @sockets: the number of sockets on the machine * @dies: the number of dies in one socket - * @cores: the number of cores in one die + * @clusters: the number of clusters in one die + * @cores: the number of cores in one cluster * @threads: the number of threads in one core * @max_cpus: the maximum number of logical processors on the machine */ @@ -307,6 +310,7 @@ typedef struct CpuTopology { unsigned int cpus; unsigned int sockets; unsigned int dies; + unsigned int clusters; unsigned int cores; unsigned int threads; unsigned int max_cpus; diff --git a/qapi/machine.json b/qapi/machine.json index f1839acf20..8faa51074e 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1396,7 +1396,9 @@ # # @dies: number of dies per socket in the CPU topology # -# @cores: number of cores per die in the CPU topology +# @clusters: number of clusters per die in the CPU topology (since 7.0) +# +# @cores: number of cores per cluster in the CPU topology # # @threads: number of threads per core in the CPU topology # @@ -1408,6 +1410,7 @@ '*cpus': 'int', '*sockets': 'int', '*dies': 'int', + '*clusters': 'int', '*cores': 'int', '*threads': 'int', '*maxcpus': 'int' } } diff --git a/qemu-options.hx b/qemu-options.hx index 7a59db7764..0f26f7dad7 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -206,13 +206,14 @@ SRST ERST DEF("smp", HAS_ARG, QEMU_OPTION_smp, - "-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]\n" + "-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]\n" " set the number of initial CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total CPUs, including\n" " offline CPUs for hotplug, etc\n" " sockets= number of sockets on the machine board\n" " dies= number of dies in one socket\n" - " cores= number of cores in one die\n" + " clusters= number of clusters in one die\n" + " cores= number of cores in one cluster\n" " threads= number of threads in one core\n" "Note: Different machines may have different subsets of the CPU topology\n" " parameters supported, so the actual meaning of the supported parameters\n" @@ -228,7 +229,7 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp, " must be set as 1 in the purpose of correct parsing.\n", QEMU_ARCH_ALL) SRST -``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]`` +``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]`` Simulate a SMP system with '\ ``n``\ ' CPUs initially present on the machine type board. On boards supporting CPU hotplug, the optional '\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be diff --git a/softmmu/vl.c b/softmmu/vl.c index 620a1f1367..d9e4c619d3 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -726,6 +726,9 @@ static QemuOptsList qemu_smp_opts = { }, { .name = "dies", .type = QEMU_OPT_NUMBER, + }, { + .name = "clusters", + .type = QEMU_OPT_NUMBER, }, { .name = "cores", .type = QEMU_OPT_NUMBER, -- Gitee From 5e8a39a560ea58308f66d47639c0d5d2e704997f Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Dec 2021 17:22:11 +0800 Subject: [PATCH 048/486] tests/unit/test-smp-parse: Add testcases for CPU clusters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add testcases for parsing of the four-level CPU topology hierarchy, ie sockets/clusters/cores/threads, which will be supported on ARM virt machines. Signed-off-by: Yanan Wang Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211228092221.21068-5-wangyanan55@huawei.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/test-smp-parse.c | 130 ++++++++++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 7 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index b6df8137fc..331719bbc4 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -61,6 +61,20 @@ .has_maxcpus = hf, .maxcpus = f, \ } +/* + * Currently a 4-level topology hierarchy is supported on ARM virt machines + * -sockets/clusters/cores/threads + */ +#define SMP_CONFIG_WITH_CLUSTERS(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \ + { \ + .has_cpus = ha, .cpus = a, \ + .has_sockets = hb, .sockets = b, \ + .has_clusters = hc, .clusters = c, \ + .has_cores = hd, .cores = d, \ + .has_threads = he, .threads = e, \ + .has_maxcpus = hf, .maxcpus = f, \ + } + /** * @config - the given SMP configuration * @expect_prefer_sockets - the expected parsing result for the @@ -290,6 +304,10 @@ static const struct SMPTestData data_generic_invalid[] = { /* config: -smp 2,dies=2 */ .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), .expect_error = "dies not supported by this machine's CPU topology", + }, { + /* config: -smp 2,clusters=2 */ + .config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), + .expect_error = "clusters not supported by this machine's CPU topology", }, { /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */ .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8), @@ -337,20 +355,40 @@ static const struct SMPTestData data_with_dies_invalid[] = { }, }; +static const struct SMPTestData data_with_clusters_invalid[] = { + { + /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_WITH_CLUSTERS(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (2) * clusters (2) * cores (4) * threads (2) " + "!= maxcpus (16)", + }, { + /* config: -smp 34,sockets=2,clusters=2,cores=4,threads=2,maxcpus=32 */ + .config = SMP_CONFIG_WITH_CLUSTERS(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "sockets (2) * clusters (2) * cores (4) * threads (2) " + "== maxcpus (32) < smp_cpus (34)", + }, +}; + static char *smp_config_to_string(const SMPConfiguration *config) { return g_strdup_printf( "(SMPConfiguration) {\n" - " .has_cpus = %5s, cpus = %" PRId64 ",\n" - " .has_sockets = %5s, sockets = %" PRId64 ",\n" - " .has_dies = %5s, dies = %" PRId64 ",\n" - " .has_cores = %5s, cores = %" PRId64 ",\n" - " .has_threads = %5s, threads = %" PRId64 ",\n" - " .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n" + " .has_cpus = %5s, cpus = %" PRId64 ",\n" + " .has_sockets = %5s, sockets = %" PRId64 ",\n" + " .has_dies = %5s, dies = %" PRId64 ",\n" + " .has_clusters = %5s, clusters = %" PRId64 ",\n" + " .has_cores = %5s, cores = %" PRId64 ",\n" + " .has_threads = %5s, threads = %" PRId64 ",\n" + " .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n" "}", config->has_cpus ? "true" : "false", config->cpus, config->has_sockets ? "true" : "false", config->sockets, config->has_dies ? "true" : "false", config->dies, + config->has_clusters ? "true" : "false", config->clusters, config->has_cores ? "true" : "false", config->cores, config->has_threads ? "true" : "false", config->threads, config->has_maxcpus ? "true" : "false", config->maxcpus); @@ -363,11 +401,12 @@ static char *cpu_topology_to_string(const CpuTopology *topo) " .cpus = %u,\n" " .sockets = %u,\n" " .dies = %u,\n" + " .clusters = %u,\n" " .cores = %u,\n" " .threads = %u,\n" " .max_cpus = %u,\n" "}", - topo->cpus, topo->sockets, topo->dies, + topo->cpus, topo->sockets, topo->dies, topo->clusters, topo->cores, topo->threads, topo->max_cpus); } @@ -391,6 +430,7 @@ static void check_parse(MachineState *ms, const SMPConfiguration *config, (ms->smp.cpus == expect_topo->cpus) && (ms->smp.sockets == expect_topo->sockets) && (ms->smp.dies == expect_topo->dies) && + (ms->smp.clusters == expect_topo->clusters) && (ms->smp.cores == expect_topo->cores) && (ms->smp.threads == expect_topo->threads) && (ms->smp.max_cpus == expect_topo->max_cpus)) { @@ -472,6 +512,11 @@ static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) data->expect_prefer_sockets.dies = 1; data->expect_prefer_cores.dies = 1; } + + if (!mc->smp_props.clusters_supported) { + data->expect_prefer_sockets.clusters = 1; + data->expect_prefer_cores.clusters = 1; + } } static void machine_base_class_init(ObjectClass *oc, void *data) @@ -491,6 +536,7 @@ static void machine_generic_valid_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS; mc->smp_props.dies_supported = false; + mc->smp_props.clusters_supported = false; } static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) @@ -502,6 +548,7 @@ static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) mc->max_cpus = 511; mc->smp_props.dies_supported = false; + mc->smp_props.clusters_supported = false; } static void machine_with_dies_class_init(ObjectClass *oc, void *data) @@ -512,6 +559,18 @@ static void machine_with_dies_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS; mc->smp_props.dies_supported = true; + mc->smp_props.clusters_supported = false; +} + +static void machine_with_clusters_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->min_cpus = MIN_CPUS; + mc->max_cpus = MAX_CPUS; + + mc->smp_props.clusters_supported = true; + mc->smp_props.dies_supported = false; } static void test_generic_valid(const void *opaque) @@ -607,6 +666,56 @@ static void test_with_dies(const void *opaque) object_unref(obj); } +static void test_with_clusters(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int num_clusters = 2; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* when clusters parameter is omitted, it will be set as 1 */ + data.expect_prefer_sockets.clusters = 1; + data.expect_prefer_cores.clusters = 1; + + smp_parse_test(ms, &data, true); + + /* when clusters parameter is specified */ + data.config.has_clusters = true; + data.config.clusters = num_clusters; + if (data.config.has_cpus) { + data.config.cpus *= num_clusters; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_clusters; + } + + data.expect_prefer_sockets.clusters = num_clusters; + data.expect_prefer_sockets.cpus *= num_clusters; + data.expect_prefer_sockets.max_cpus *= num_clusters; + data.expect_prefer_cores.clusters = num_clusters; + data.expect_prefer_cores.cpus *= num_clusters; + data.expect_prefer_cores.max_cpus *= num_clusters; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_clusters_invalid); i++) { + data = data_with_clusters_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + /* Type info of the tested machine */ static const TypeInfo smp_machine_types[] = { { @@ -628,6 +737,10 @@ static const TypeInfo smp_machine_types[] = { .name = MACHINE_TYPE_NAME("smp-with-dies"), .parent = TYPE_MACHINE, .class_init = machine_with_dies_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-clusters"), + .parent = TYPE_MACHINE, + .class_init = machine_with_clusters_class_init, } }; @@ -648,6 +761,9 @@ int main(int argc, char *argv[]) g_test_add_data_func("/test-smp-parse/with_dies", MACHINE_TYPE_NAME("smp-with-dies"), test_with_dies); + g_test_add_data_func("/test-smp-parse/with_clusters", + MACHINE_TYPE_NAME("smp-with-clusters"), + test_with_clusters); g_test_run(); -- Gitee From 77bca7d51e99f8ba4d11635ff9f51615739f4d55 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Dec 2021 17:22:12 +0800 Subject: [PATCH 049/486] tests/unit/test-smp-parse: No need to explicitly zero MachineClass members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default value of the MachineClass members is 0, which means we don't have to explicitly zero them. Also the value of "mc->smp_props.prefer_sockets" will be taken care of by smp_parse_test(), we don't necessarily need the statement in machine_base_class_init() either. Signed-off-by: Yanan Wang Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211228092221.21068-6-wangyanan55@huawei.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/test-smp-parse.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 331719bbc4..72d83d1bbc 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -523,8 +523,6 @@ static void machine_base_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->smp_props.prefer_sockets = true; - mc->name = g_strdup(SMP_MACHINE_NAME); } @@ -534,9 +532,6 @@ static void machine_generic_valid_class_init(ObjectClass *oc, void *data) mc->min_cpus = MIN_CPUS; mc->max_cpus = MAX_CPUS; - - mc->smp_props.dies_supported = false; - mc->smp_props.clusters_supported = false; } static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) @@ -546,9 +541,6 @@ static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) /* Force invalid min CPUs and max CPUs */ mc->min_cpus = 2; mc->max_cpus = 511; - - mc->smp_props.dies_supported = false; - mc->smp_props.clusters_supported = false; } static void machine_with_dies_class_init(ObjectClass *oc, void *data) @@ -559,7 +551,6 @@ static void machine_with_dies_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS; mc->smp_props.dies_supported = true; - mc->smp_props.clusters_supported = false; } static void machine_with_clusters_class_init(ObjectClass *oc, void *data) @@ -570,7 +561,6 @@ static void machine_with_clusters_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS; mc->smp_props.clusters_supported = true; - mc->smp_props.dies_supported = false; } static void test_generic_valid(const void *opaque) -- Gitee From 214511b1799b94cfd514a222d087bb888ed808ba Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 28 Dec 2021 17:22:13 +0800 Subject: [PATCH 050/486] tests/unit/test-smp-parse: Keep default MIN/MAX CPUs in machine_base_class_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most machine types in test-smp-parse will be OK to have the default MIN/MAX CPUs except "smp-generic-invalid", let's keep the default values in machine_base_class_init which will be inherited. And if we hope a different value for a specific machine, modify it in its own initialization function. Signed-off-by: Yanan Wang Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20211228092221.21068-7-wangyanan55@huawei.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/test-smp-parse.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 72d83d1bbc..fdc39a846c 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -523,15 +523,10 @@ static void machine_base_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->name = g_strdup(SMP_MACHINE_NAME); -} - -static void machine_generic_valid_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - mc->min_cpus = MIN_CPUS; mc->max_cpus = MAX_CPUS; + + mc->name = g_strdup(SMP_MACHINE_NAME); } static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) @@ -547,9 +542,6 @@ static void machine_with_dies_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->min_cpus = MIN_CPUS; - mc->max_cpus = MAX_CPUS; - mc->smp_props.dies_supported = true; } @@ -557,9 +549,6 @@ static void machine_with_clusters_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->min_cpus = MIN_CPUS; - mc->max_cpus = MAX_CPUS; - mc->smp_props.clusters_supported = true; } @@ -718,7 +707,6 @@ static const TypeInfo smp_machine_types[] = { }, { .name = MACHINE_TYPE_NAME("smp-generic-valid"), .parent = TYPE_MACHINE, - .class_init = machine_generic_valid_class_init, }, { .name = MACHINE_TYPE_NAME("smp-generic-invalid"), .parent = TYPE_MACHINE, -- Gitee From 1fab7ee365c8daccedd19d3a1be56babe36afcc6 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 7 Jan 2022 16:32:27 +0800 Subject: [PATCH 051/486] hw/arm/virt: Support CPU cluster on ARM virt machine ARM64 machines like Kunpeng Family Server Chips have a level of hardware topology in which a group of CPU cores share L3 cache tag or L2 cache. For example, Kunpeng 920 typically has 6 or 8 clusters in each NUMA node (also represent range of CPU die), and each cluster has 4 CPU cores. All clusters share L3 cache data, but CPU cores in each cluster share a local L3 tag. Running a guest kernel with Cluster-Aware Scheduling on the Hosts which have physical clusters, if we can design a vCPU topology with cluster level for guest kernel and then have a dedicated vCPU pinning, the guest will gain scheduling performance improvement from cache affinity of CPU cluster. So let's enable the support for this new parameter on ARM virt machines. After this patch, we can define a 4-level CPU hierarchy like: cpus=*,maxcpus=*,sockets=*,clusters=*, cores=*,threads=*. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Message-id: 20220107083232.16256-2-wangyanan55@huawei.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 1 + qemu-options.hx | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3c972fdab0..6ca9cbe2cf 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2704,6 +2704,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->unplug_request = virt_machine_device_unplug_request_cb; hc->unplug = virt_machine_device_unplug_cb; mc->nvdimm_supported = true; + mc->smp_props.clusters_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; mc->default_ram_id = "mach-virt.ram"; diff --git a/qemu-options.hx b/qemu-options.hx index 0f26f7dad7..74d335e4c3 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -277,6 +277,16 @@ SRST -smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 + The following sub-option defines a CPU topology hierarchy (2 sockets + totally on the machine, 2 clusters per socket, 2 cores per cluster, + 2 threads per core) for ARM virt machines which support sockets/clusters + /cores/threads. Some members of the option can be omitted but their values + will be automatically computed: + + :: + + -smp 16,sockets=2,clusters=2,cores=2,threads=2,maxcpus=16 + Historically preference was given to the coarsest topology parameters when computing missing values (ie sockets preferred over cores, which were preferred over threads), however, this behaviour is considered -- Gitee From 38d9ae59b9344f13198e6b4de03b04787bd6b89d Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 7 Jan 2022 16:32:28 +0800 Subject: [PATCH 052/486] hw/arm/virt: Support cluster level in DT cpu-map Support one cluster level between core and physical package in the cpu-map of Arm/virt devicetree. This is also consistent with Linux Doc "Documentation/devicetree/bindings/cpu/cpu-topology.txt". Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Message-id: 20220107083232.16256-3-wangyanan55@huawei.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6ca9cbe2cf..ddcb73f714 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -434,9 +434,8 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) * can contain several layers of clustering within a single physical * package and cluster nodes can be contained in parent cluster nodes. * - * Given that cluster is not yet supported in the vCPU topology, - * we currently generate one cluster node within each socket node - * by default. + * Note: currently we only support one layer of clustering within + * each physical package. */ qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); @@ -446,14 +445,16 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) if (ms->smp.threads > 1) { map_path = g_strdup_printf( - "/cpus/cpu-map/socket%d/cluster0/core%d/thread%d", - cpu / (ms->smp.cores * ms->smp.threads), + "/cpus/cpu-map/socket%d/cluster%d/core%d/thread%d", + cpu / (ms->smp.clusters * ms->smp.cores * ms->smp.threads), + (cpu / (ms->smp.cores * ms->smp.threads)) % ms->smp.clusters, (cpu / ms->smp.threads) % ms->smp.cores, cpu % ms->smp.threads); } else { map_path = g_strdup_printf( - "/cpus/cpu-map/socket%d/cluster0/core%d", - cpu / ms->smp.cores, + "/cpus/cpu-map/socket%d/cluster%d/core%d", + cpu / (ms->smp.clusters * ms->smp.cores), + (cpu / ms->smp.cores) % ms->smp.clusters, cpu % ms->smp.cores); } qemu_fdt_add_path(ms->fdt, map_path); -- Gitee From 66c935b435d90ef9c1ae4446c5edc07cbd8ba0ed Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 7 Jan 2022 16:32:29 +0800 Subject: [PATCH 053/486] hw/acpi/aml-build: Improve scalability of PPTT generation Use g_queue APIs to reduce the nested loops and code indentation with the processor hierarchy levels increasing. Consenquently, it's more scalable to add new topology level to build_pptt. No functional change intended. Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Message-id: 20220107083232.16256-4-wangyanan55@huawei.com Signed-off-by: Peter Maydell --- hw/acpi/aml-build.c | 50 +++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index b3b3310df3..6aaedca2e5 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2001,7 +2001,10 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, const char *oem_id, const char *oem_table_id) { - int pptt_start = table_data->len; + GQueue *list = g_queue_new(); + guint pptt_start = table_data->len; + guint parent_offset; + guint length, i; int uid = 0; int socket; AcpiTable table = { .sig = "PPTT", .rev = 2, @@ -2010,9 +2013,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, acpi_table_begin(&table, table_data); for (socket = 0; socket < ms->smp.sockets; socket++) { - uint32_t socket_offset = table_data->len - pptt_start; - int core; - + g_queue_push_tail(list, + GUINT_TO_POINTER(table_data->len - pptt_start)); build_processor_hierarchy_node( table_data, /* @@ -2021,35 +2023,47 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, */ (1 << 0), 0, socket, NULL, 0); + } - for (core = 0; core < ms->smp.cores; core++) { - uint32_t core_offset = table_data->len - pptt_start; - int thread; + length = g_queue_get_length(list); + for (i = 0; i < length; i++) { + int core; + parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list)); + for (core = 0; core < ms->smp.cores; core++) { if (ms->smp.threads > 1) { + g_queue_push_tail(list, + GUINT_TO_POINTER(table_data->len - pptt_start)); build_processor_hierarchy_node( table_data, (0 << 0), /* not a physical package */ - socket_offset, core, NULL, 0); - - for (thread = 0; thread < ms->smp.threads; thread++) { - build_processor_hierarchy_node( - table_data, - (1 << 1) | /* ACPI Processor ID valid */ - (1 << 2) | /* Processor is a Thread */ - (1 << 3), /* Node is a Leaf */ - core_offset, uid++, NULL, 0); - } + parent_offset, core, NULL, 0); } else { build_processor_hierarchy_node( table_data, (1 << 1) | /* ACPI Processor ID valid */ (1 << 3), /* Node is a Leaf */ - socket_offset, uid++, NULL, 0); + parent_offset, uid++, NULL, 0); } } } + length = g_queue_get_length(list); + for (i = 0; i < length; i++) { + int thread; + + parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list)); + for (thread = 0; thread < ms->smp.threads; thread++) { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 2) | /* Processor is a Thread */ + (1 << 3), /* Node is a Leaf */ + parent_offset, uid++, NULL, 0); + } + } + + g_queue_free(list); acpi_table_end(linker, &table); } -- Gitee From 225034a72c803b8e3819cec22bc6fb8bfc9e7366 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 7 Jan 2022 16:32:30 +0800 Subject: [PATCH 054/486] tests/acpi/bios-tables-test: Allow changes to virt/PPTT file List test/data/acpi/virt/PPTT as the expected files allowed to be changed in tests/qtest/bios-tables-test-allowed-diff.h Signed-off-by: Yanan Wang Acked-by: Ani Sinha Message-id: 20220107083232.16256-5-wangyanan55@huawei.com Signed-off-by: Peter Maydell --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..cb143a55a6 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/PPTT", -- Gitee From 9c16924ba0a77c34246b69e8b1faee219f266445 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 7 Jan 2022 16:32:31 +0800 Subject: [PATCH 055/486] hw/acpi/aml-build: Support cluster level in PPTT generation Support CPU cluster topology level in generation of ACPI Processor Properties Topology Table (PPTT). Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Message-id: 20220107083232.16256-6-wangyanan55@huawei.com Signed-off-by: Peter Maydell --- hw/acpi/aml-build.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 6aaedca2e5..bb2cad63b5 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2001,6 +2001,7 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, const char *oem_id, const char *oem_table_id) { + MachineClass *mc = MACHINE_GET_CLASS(ms); GQueue *list = g_queue_new(); guint pptt_start = table_data->len; guint parent_offset; @@ -2025,6 +2026,23 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, 0, socket, NULL, 0); } + if (mc->smp_props.clusters_supported) { + length = g_queue_get_length(list); + for (i = 0; i < length; i++) { + int cluster; + + parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list)); + for (cluster = 0; cluster < ms->smp.clusters; cluster++) { + g_queue_push_tail(list, + GUINT_TO_POINTER(table_data->len - pptt_start)); + build_processor_hierarchy_node( + table_data, + (0 << 0), /* not a physical package */ + parent_offset, cluster, NULL, 0); + } + } + } + length = g_queue_get_length(list); for (i = 0; i < length; i++) { int core; -- Gitee From 6f89f06e686a61acf681038ac06732facc6e7b93 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Fri, 7 Jan 2022 16:32:32 +0800 Subject: [PATCH 056/486] tests/acpi/bios-table-test: Update expected virt/PPTT file Run ./tests/data/acpi/rebuild-expected-aml.sh from build directory to update PPTT binary. Also empty bios-tables-test-allowed-diff.h. The disassembled differences between actual and expected PPTT: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200528 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/virt/PPTT, Tue Jan 4 12:51:11 2022 + * Disassembly of /tmp/aml-2ZGOF1, Tue Jan 4 12:51:11 2022 * * ACPI Data Table [PPTT] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "PPTT" [Processor Properties Topology Table] -[004h 0004 4] Table Length : 0000004C +[004h 0004 4] Table Length : 00000060 [008h 0008 1] Revision : 02 -[009h 0009 1] Checksum : A8 +[009h 0009 1] Checksum : 48 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 1] Subtable Type : 00 [Processor Hierarchy Node] [025h 0037 1] Length : 14 [026h 0038 2] Reserved : 0000 [028h 0040 4] Flags (decoded below) : 00000001 Physical package : 1 ACPI Processor ID valid : 0 Processor is a thread : 0 Node is a leaf : 0 Identical Implementation : 0 [02Ch 0044 4] Parent : 00000000 [030h 0048 4] ACPI Processor ID : 00000000 [034h 0052 4] Private Resource Number : 00000000 [038h 0056 1] Subtable Type : 00 [Processor Hierarchy Node] [039h 0057 1] Length : 14 [03Ah 0058 2] Reserved : 0000 -[03Ch 0060 4] Flags (decoded below) : 0000000A +[03Ch 0060 4] Flags (decoded below) : 00000000 Physical package : 0 - ACPI Processor ID valid : 1 + ACPI Processor ID valid : 0 Processor is a thread : 0 - Node is a leaf : 1 + Node is a leaf : 0 Identical Implementation : 0 [040h 0064 4] Parent : 00000024 [044h 0068 4] ACPI Processor ID : 00000000 [048h 0072 4] Private Resource Number : 00000000 -Raw Table Data: Length 76 (0x4C) +[04Ch 0076 1] Subtable Type : 00 [Processor Hierarchy Node] +[04Dh 0077 1] Length : 14 +[04Eh 0078 2] Reserved : 0000 +[050h 0080 4] Flags (decoded below) : 0000000A + Physical package : 0 + ACPI Processor ID valid : 1 + Processor is a thread : 0 + Node is a leaf : 1 + Identical Implementation : 0 +[054h 0084 4] Parent : 00000038 +[058h 0088 4] ACPI Processor ID : 00000000 +[05Ch 0092 4] Private Resource Number : 00000000 + +Raw Table Data: Length 96 (0x60) - 0000: 50 50 54 54 4C 00 00 00 02 A8 42 4F 43 48 53 20 // PPTTL.....BOCHS + 0000: 50 50 54 54 60 00 00 00 02 48 42 4F 43 48 53 20 // PPTT`....HBOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC 0020: 01 00 00 00 00 14 00 00 01 00 00 00 00 00 00 00 // ................ - 0030: 00 00 00 00 00 00 00 00 00 14 00 00 0A 00 00 00 // ................ - 0040: 24 00 00 00 00 00 00 00 00 00 00 00 // $........... + 0030: 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 // ................ + 0040: 24 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 // $............... + 0050: 0A 00 00 00 38 00 00 00 00 00 00 00 00 00 00 00 // ....8........... Signed-off-by: Yanan Wang Reviewed-by: Ani Sinha Message-id: 20220107083232.16256-7-wangyanan55@huawei.com Signed-off-by: Peter Maydell --- tests/data/acpi/virt/PPTT | Bin 76 -> 96 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/virt/PPTT index 7a1258ecf123555b24462c98ccbb76b4ac1d0c2b..f56ea63b369a604877374ad696c396e796ab1c83 100644 GIT binary patch delta 53 pcmeZC;0g!`2}xjJU|{l?$YrDgWH5jU5Ca567#O&Klm(arApowi1QY-O delta 32 fcmYfB;R*-{3GrcIU|?D?k;`ae01J-_kOKn%ZFdCM diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index cb143a55a6..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/virt/PPTT", -- Gitee From ecc0eb93e8856321ad940a85970f0db14ab9f146 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 7 Jan 2022 14:38:44 +0100 Subject: [PATCH 057/486] softmmu/device_tree: Silence compiler warning with --enable-sanitizers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If I configure my build with --enable-sanitizers, my GCC (v8.5.0) complains: .../softmmu/device_tree.c: In function ‘qemu_fdt_add_path’: .../softmmu/device_tree.c:560:18: error: ‘retval’ may be used uninitialized in this function [-Werror=maybe-uninitialized] int namelen, retval; ^~~~~~ It's a false warning since the while loop is always executed at least once (p has to be non-NULL, otherwise the derefence in the if-statement earlier will crash). Thus let's switch to a do-while loop here instead to make the compiler happy in all cases. Signed-off-by: Thomas Huth Reviewed-by: Andrew Jones Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Yanan Wang Message-id: 20220107133844.145039-1-thuth@redhat.com Signed-off-by: Alistair Francis --- softmmu/device_tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/softmmu/device_tree.c b/softmmu/device_tree.c index 3965c834ca..9e96f5ecd5 100644 --- a/softmmu/device_tree.c +++ b/softmmu/device_tree.c @@ -564,7 +564,7 @@ int qemu_fdt_add_path(void *fdt, const char *path) return -1; } - while (p) { + do { name = p + 1; p = strchr(name, '/'); namelen = p != NULL ? p - name : strlen(name); @@ -584,7 +584,7 @@ int qemu_fdt_add_path(void *fdt, const char *path) } parent = retval; - } + } while (p); return retval; } -- Gitee From ebf1ac6c0ead3d6fbc32466028c286588333c1ea Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Tue, 11 Jan 2022 11:27:58 +0800 Subject: [PATCH 058/486] softmmu/device_tree: Remove redundant pointer assignment The pointer assignment "const char *p = path;" in function qemu_fdt_add_path is unnecessary. Let's remove it and just use the "path" passed in. No functional change. Suggested-by: Richard Henderson Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Reviewed-by: Thomas Huth Message-id: 20220111032758.27804-1-wangyanan55@huawei.com Signed-off-by: Alistair Francis --- softmmu/device_tree.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/softmmu/device_tree.c b/softmmu/device_tree.c index 9e96f5ecd5..8897c79ea4 100644 --- a/softmmu/device_tree.c +++ b/softmmu/device_tree.c @@ -556,7 +556,6 @@ int qemu_fdt_add_subnode(void *fdt, const char *name) int qemu_fdt_add_path(void *fdt, const char *path) { const char *name; - const char *p = path; int namelen, retval; int parent = 0; @@ -565,9 +564,9 @@ int qemu_fdt_add_path(void *fdt, const char *path) } do { - name = p + 1; - p = strchr(name, '/'); - namelen = p != NULL ? p - name : strlen(name); + name = path + 1; + path = strchr(name, '/'); + namelen = path != NULL ? path - name : strlen(name); retval = fdt_subnode_offset_namelen(fdt, parent, name, namelen); if (retval < 0 && retval != -FDT_ERR_NOTFOUND) { @@ -584,7 +583,7 @@ int qemu_fdt_add_path(void *fdt, const char *path) } parent = retval; - } while (p); + } while (path); return retval; } -- Gitee From c5cd762bb7513b6df07e26f4eb619dccbd1918b7 Mon Sep 17 00:00:00 2001 From: Ying Fang Date: Tue, 8 Feb 2022 11:31:15 +0800 Subject: [PATCH 059/486] hw/arm64: add vcpu cache info support Support VCPU Cache info by dtb and PPTT table, including L1, L2 and L3 Cache. Signed-off-by: zhanghailiang Signed-off-by: Honghao Signed-off-by: Ying Fang Signed-off-by: Yanan Wang --- hw/acpi/aml-build.c | 158 ++++++++++++++++++++++++++++++++++++ hw/arm/virt.c | 72 ++++++++++++++++ include/hw/acpi/aml-build.h | 47 +++++++++++ tests/data/acpi/virt/PPTT | Bin 96 -> 208 bytes 4 files changed, 277 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index bb2cad63b5..bebf49622b 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1994,6 +1994,163 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, } } +#ifdef __aarch64__ +/* + * ACPI spec, Revision 6.3 + * 5.2.29.2 Cache Type Structure (Type 1) + */ +static void build_cache_hierarchy_node(GArray *tbl, uint32_t next_level, + uint32_t cache_type) +{ + build_append_byte(tbl, 1); + build_append_byte(tbl, 24); + build_append_int_noprefix(tbl, 0, 2); + build_append_int_noprefix(tbl, 127, 4); + build_append_int_noprefix(tbl, next_level, 4); + + switch (cache_type) { + case ARM_L1D_CACHE: /* L1 dcache info */ + build_append_int_noprefix(tbl, ARM_L1DCACHE_SIZE, 4); + build_append_int_noprefix(tbl, ARM_L1DCACHE_SETS, 4); + build_append_byte(tbl, ARM_L1DCACHE_ASSOCIATIVITY); + build_append_byte(tbl, ARM_L1DCACHE_ATTRIBUTES); + build_append_int_noprefix(tbl, ARM_L1DCACHE_LINE_SIZE, 2); + break; + case ARM_L1I_CACHE: /* L1 icache info */ + build_append_int_noprefix(tbl, ARM_L1ICACHE_SIZE, 4); + build_append_int_noprefix(tbl, ARM_L1ICACHE_SETS, 4); + build_append_byte(tbl, ARM_L1ICACHE_ASSOCIATIVITY); + build_append_byte(tbl, ARM_L1ICACHE_ATTRIBUTES); + build_append_int_noprefix(tbl, ARM_L1ICACHE_LINE_SIZE, 2); + break; + case ARM_L2_CACHE: /* L2 cache info */ + build_append_int_noprefix(tbl, ARM_L2CACHE_SIZE, 4); + build_append_int_noprefix(tbl, ARM_L2CACHE_SETS, 4); + build_append_byte(tbl, ARM_L2CACHE_ASSOCIATIVITY); + build_append_byte(tbl, ARM_L2CACHE_ATTRIBUTES); + build_append_int_noprefix(tbl, ARM_L2CACHE_LINE_SIZE, 2); + break; + case ARM_L3_CACHE: /* L3 cache info */ + build_append_int_noprefix(tbl, ARM_L3CACHE_SIZE, 4); + build_append_int_noprefix(tbl, ARM_L3CACHE_SETS, 4); + build_append_byte(tbl, ARM_L3CACHE_ASSOCIATIVITY); + build_append_byte(tbl, ARM_L3CACHE_ATTRIBUTES); + build_append_int_noprefix(tbl, ARM_L3CACHE_LINE_SIZE, 2); + break; + default: + build_append_int_noprefix(tbl, 0, 4); + build_append_int_noprefix(tbl, 0, 4); + build_append_byte(tbl, 0); + build_append_byte(tbl, 0); + build_append_int_noprefix(tbl, 0, 2); + } +} + +/* + * ACPI spec, Revision 6.3 + * 5.2.29 Processor Properties Topology Table (PPTT) + */ +void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, + const char *oem_id, const char *oem_table_id) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + GQueue *list = g_queue_new(); + guint pptt_start = table_data->len; + guint parent_offset; + guint length, i; + int uid = 0; + int socket; + AcpiTable table = { .sig = "PPTT", .rev = 2, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + + acpi_table_begin(&table, table_data); + + for (socket = 0; socket < ms->smp.sockets; socket++) { + uint32_t l3_cache_offset = table_data->len - pptt_start; + build_cache_hierarchy_node(table_data, 0, ARM_L3_CACHE); + + g_queue_push_tail(list, + GUINT_TO_POINTER(table_data->len - pptt_start)); + build_processor_hierarchy_node( + table_data, + /* + * Physical package - represents the boundary + * of a physical package + */ + (1 << 0), + 0, socket, &l3_cache_offset, 1); + } + + if (mc->smp_props.clusters_supported) { + length = g_queue_get_length(list); + for (i = 0; i < length; i++) { + int cluster; + + parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list)); + for (cluster = 0; cluster < ms->smp.clusters; cluster++) { + g_queue_push_tail(list, + GUINT_TO_POINTER(table_data->len - pptt_start)); + build_processor_hierarchy_node( + table_data, + (0 << 0), /* not a physical package */ + parent_offset, cluster, NULL, 0); + } + } + } + + length = g_queue_get_length(list); + for (i = 0; i < length; i++) { + int core; + + parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list)); + for (core = 0; core < ms->smp.cores; core++) { + uint32_t priv_rsrc[3] = {}; + priv_rsrc[0] = table_data->len - pptt_start; /* L2 cache offset */ + build_cache_hierarchy_node(table_data, 0, ARM_L2_CACHE); + + priv_rsrc[1] = table_data->len - pptt_start; /* L1 dcache offset */ + build_cache_hierarchy_node(table_data, priv_rsrc[0], ARM_L1D_CACHE); + + priv_rsrc[2] = table_data->len - pptt_start; /* L1 icache offset */ + build_cache_hierarchy_node(table_data, priv_rsrc[0], ARM_L1I_CACHE); + + if (ms->smp.threads > 1) { + g_queue_push_tail(list, + GUINT_TO_POINTER(table_data->len - pptt_start)); + build_processor_hierarchy_node( + table_data, + (0 << 0), /* not a physical package */ + parent_offset, core, priv_rsrc, 3); + } else { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 3), /* Node is a Leaf */ + parent_offset, uid++, priv_rsrc, 3); + } + } + } + + length = g_queue_get_length(list); + for (i = 0; i < length; i++) { + int thread; + + parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list)); + for (thread = 0; thread < ms->smp.threads; thread++) { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 2) | /* Processor is a Thread */ + (1 << 3), /* Node is a Leaf */ + parent_offset, uid++, NULL, 0); + } + } + + g_queue_free(list); + acpi_table_end(linker, &table); +} + +#else /* * ACPI spec, Revision 6.3 * 5.2.29 Processor Properties Topology Table (PPTT) @@ -2084,6 +2241,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, g_queue_free(list); acpi_table_end(linker, &table); } +#endif /* build rev1/rev3/rev5.1 FADT */ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ddcb73f714..529c0d38b6 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -350,6 +350,72 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL2_IRQ, irqflags); } +static void fdt_add_l3cache_nodes(const VirtMachineState *vms) +{ + int i; + const MachineState *ms = MACHINE(vms); + int cpus_per_socket = ms->smp.clusters * ms->smp.cores * ms->smp.threads; + int sockets = (ms->smp.cpus + cpus_per_socket - 1) / cpus_per_socket; + + for (i = 0; i < sockets; i++) { + char *nodename = g_strdup_printf("/cpus/l3-cache%d", i); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cache"); + qemu_fdt_setprop_string(ms->fdt, nodename, "cache-unified", "true"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-level", 3); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-size", 0x2000000); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-line-size", 128); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-sets", 2048); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + g_free(nodename); + } +} + +static void fdt_add_l2cache_nodes(const VirtMachineState *vms) +{ + const MachineState *ms = MACHINE(vms); + int cpus_per_socket = ms->smp.clusters * ms->smp.cores * ms->smp.threads; + int cpu; + + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + char *next_path = g_strdup_printf("/cpus/l3-cache%d", + cpu / cpus_per_socket); + char *nodename = g_strdup_printf("/cpus/l2-cache%d", cpu); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cache"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-size", 0x80000); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-line-size", 64); + qemu_fdt_setprop_cell(ms->fdt, nodename, "cache-sets", 1024); + qemu_fdt_setprop_phandle(ms->fdt, nodename, "next-level-cache", + next_path); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + + g_free(next_path); + g_free(nodename); + } +} + +static void fdt_add_l1cache_prop(const VirtMachineState *vms, + char *nodename, int cpu) +{ + const MachineState *ms = MACHINE(vms); + char *cachename = g_strdup_printf("/cpus/l2-cache%d", cpu); + + qemu_fdt_setprop_cell(ms->fdt, nodename, "d-cache-size", 0x10000); + qemu_fdt_setprop_cell(ms->fdt, nodename, "d-cache-line-size", 64); + qemu_fdt_setprop_cell(ms->fdt, nodename, "d-cache-sets", 256); + qemu_fdt_setprop_cell(ms->fdt, nodename, "i-cache-size", 0x10000); + qemu_fdt_setprop_cell(ms->fdt, nodename, "i-cache-line-size", 64); + qemu_fdt_setprop_cell(ms->fdt, nodename, "i-cache-sets", 256); + qemu_fdt_setprop_phandle(ms->fdt, nodename, "next-level-cache", + cachename); + g_free(cachename); +} + static void fdt_add_cpu_nodes(const VirtMachineState *vms) { int cpu; @@ -384,6 +450,11 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", addr_cells); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); + if (!vmc->no_cpu_topology) { + fdt_add_l3cache_nodes(vms); + fdt_add_l2cache_nodes(vms); + } + for (cpu = smp_cpus - 1; cpu >= 0; cpu--) { char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); @@ -413,6 +484,7 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) } if (!vmc->no_cpu_topology) { + fdt_add_l1cache_prop(vms, nodename, cpu); qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", qemu_fdt_alloc_phandle(ms->fdt)); } diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 8346003a22..8e8ad8029e 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -221,6 +221,53 @@ struct AcpiBuildTables { BIOSLinker *linker; } AcpiBuildTables; +#ifdef __aarch64__ +/* Definitions of the hardcoded cache info*/ + +typedef enum { + ARM_L1D_CACHE, + ARM_L1I_CACHE, + ARM_L2_CACHE, + ARM_L3_CACHE +} ArmCacheType; + +/* L1 data cache: */ +#define ARM_L1DCACHE_SIZE 65536 +#define ARM_L1DCACHE_SETS 256 +#define ARM_L1DCACHE_ASSOCIATIVITY 4 +#define ARM_L1DCACHE_ATTRIBUTES 2 +#define ARM_L1DCACHE_LINE_SIZE 64 + +/* L1 instruction cache: */ +#define ARM_L1ICACHE_SIZE 65536 +#define ARM_L1ICACHE_SETS 256 +#define ARM_L1ICACHE_ASSOCIATIVITY 4 +#define ARM_L1ICACHE_ATTRIBUTES 4 +#define ARM_L1ICACHE_LINE_SIZE 64 + +/* Level 2 unified cache: */ +#define ARM_L2CACHE_SIZE 524288 +#define ARM_L2CACHE_SETS 1024 +#define ARM_L2CACHE_ASSOCIATIVITY 8 +#define ARM_L2CACHE_ATTRIBUTES 10 +#define ARM_L2CACHE_LINE_SIZE 64 + +/* Level 3 unified cache: */ +#define ARM_L3CACHE_SIZE 33554432 +#define ARM_L3CACHE_SETS 2048 +#define ARM_L3CACHE_ASSOCIATIVITY 15 +#define ARM_L3CACHE_ATTRIBUTES 10 +#define ARM_L3CACHE_LINE_SIZE 128 + +struct offset_status { + uint32_t parent; + uint32_t l2_offset; + uint32_t l1d_offset; + uint32_t l1i_offset; +}; + +#endif + typedef struct CrsRangeEntry { uint64_t base; diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/virt/PPTT index f56ea63b369a604877374ad696c396e796ab1c83..b89b2a9c71e0bc2713fc38f5de68fbc39b6302cb 100644 GIT binary patch literal 208 zcmWFt2no5sz`($y>E!S15v<@85#X!<1dKp25F11@N-!|g18FE=V&Gt4;OA;!U;v7P z1UT7A73xEDj6|3JeTfK(!%23G#4~^x046;S0RR91 literal 96 zcmWFt2nk7GU|?YKaPoKd2v%^42yj*a0!E-1hz+6{L>L&rG>8oYKrs+dflv?b3 ISs;l402v7f0RR91 -- Gitee From e7e28e79988eb671051d0d2af0eb010314c83d41 Mon Sep 17 00:00:00 2001 From: Ying Fang Date: Tue, 8 Feb 2022 21:01:09 +0800 Subject: [PATCH 060/486] arm64: Add the cpufreq device to show cpufreq info to guest On ARM64 platform, cpu frequency is retrieved via ACPI CPPC. A virtual cpufreq device based on ACPI CPPC is created to present cpu frequency info to the guest. The default frequency is set to host cpu nominal frequency, which is obtained from the host CPPC sysfs. Other performance data are set to the same value, since we don't support guest performance scaling here. Performance counters are also not emulated and they simply return 1 if read, and guest should fallback to use desired performance value as the current performance. Guest kernel version above 4.18 is required to make it work. This series is backported from: https://patchwork.kernel.org/cover/11379943/ Signed-off-by: Ying Fang Signed-off-by: Yanan Wang --- configs/devices/aarch64-softmmu/default.mak | 1 + hw/acpi/aml-build.c | 22 ++ hw/acpi/cpufreq.c | 283 ++++++++++++++++++++ hw/acpi/meson.build | 1 + hw/arm/virt-acpi-build.c | 77 +++++- hw/arm/virt.c | 13 + hw/char/Kconfig | 4 + include/hw/acpi/acpi-defs.h | 38 +++ include/hw/acpi/aml-build.h | 3 + include/hw/arm/virt.h | 1 + tests/data/acpi/virt/DSDT | Bin 5196 -> 5669 bytes tests/data/acpi/virt/DSDT.memhp | Bin 6557 -> 7030 bytes tests/data/acpi/virt/DSDT.numamem | Bin 5196 -> 5669 bytes tests/data/acpi/virt/DSDT.pxb | Bin 7679 -> 8152 bytes 14 files changed, 441 insertions(+), 2 deletions(-) create mode 100644 hw/acpi/cpufreq.c diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak index cf43ac8da1..c7a710a0f1 100644 --- a/configs/devices/aarch64-softmmu/default.mak +++ b/configs/devices/aarch64-softmmu/default.mak @@ -6,3 +6,4 @@ include ../arm-softmmu/default.mak CONFIG_XLNX_ZYNQMP_ARM=y CONFIG_XLNX_VERSAL=y CONFIG_SBSA_REF=y +CONFIG_CPUFREQ=y diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index bebf49622b..c4edaafa4a 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1554,6 +1554,28 @@ Aml *aml_sleep(uint64_t msec) return var; } +/* ACPI 5.0b: 6.4.3.7 Generic Register Descriptor */ +Aml *aml_generic_register(AmlRegionSpace rs, uint8_t reg_width, + uint8_t reg_offset, AmlAccessType type, uint64_t addr) +{ + int i; + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x82); /* Generic Register Descriptor */ + build_append_byte(var->buf, 0x0C); /* Length, bits[7:0] value = 0x0C */ + build_append_byte(var->buf, 0); /* Length, bits[15:8] value = 0 */ + build_append_byte(var->buf, rs); /* Address Space ID */ + build_append_byte(var->buf, reg_width); /* Register Bit Width */ + build_append_byte(var->buf, reg_offset); /* Register Bit Offset */ + build_append_byte(var->buf, type); /* Access Size */ + + /* Register address */ + for (i = 0; i < 8; i++) { + build_append_byte(var->buf, extract64(addr, i * 8, 8)); + } + + return var; +} + static uint8_t Hex2Byte(const char *src) { int hi, lo; diff --git a/hw/acpi/cpufreq.c b/hw/acpi/cpufreq.c new file mode 100644 index 0000000000..a84db490b3 --- /dev/null +++ b/hw/acpi/cpufreq.c @@ -0,0 +1,283 @@ +/* + * ACPI CPPC register device + * + * Support for showing CPU frequency in guest OS. + * + * Copyright (c) 2019 HUAWEI TECHNOLOGIES CO.,LTD. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "chardev/char.h" +#include "qemu/log.h" +#include "trace.h" +#include "qemu/option.h" +#include "sysemu/sysemu.h" +#include "hw/acpi/acpi-defs.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "hw/boards.h" + +#define TYPE_CPUFREQ "cpufreq" +#define CPUFREQ(obj) OBJECT_CHECK(CpuhzState, (obj), TYPE_CPUFREQ) +#define NOMINAL_FREQ_FILE "/sys/devices/system/cpu/cpu0/acpi_cppc/nominal_freq" +#define CPU_MAX_FREQ_FILE "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" +#define HZ_MAX_LENGTH 1024 +#define MAX_SUPPORT_SPACE 0x10000 + +/* + * Since Hi1616 will not support CPPC, we simply use its nominal frequency as + * the default. + */ +#define DEFAULT_HZ 2400 + +int cppc_regs_offset[CPPC_REG_COUNT] = { + [HIGHEST_PERF] = 0, + [NOMINAL_PERF] = 4, + [LOW_NON_LINEAR_PERF] = 8, + [LOWEST_PERF] = 12, + [GUARANTEED_PERF] = 16, + [DESIRED_PERF] = 20, + [MIN_PERF] = -1, + [MAX_PERF] = -1, + [PERF_REDUC_TOLERANCE] = -1, + [TIME_WINDOW] = -1, + [CTR_WRAP_TIME] = -1, + [REFERENCE_CTR] = 24, + [DELIVERED_CTR] = 32, + [PERF_LIMITED] = 40, + [ENABLE] = -1, + [AUTO_SEL_ENABLE] = -1, + [AUTO_ACT_WINDOW] = -1, + [ENERGY_PERF] = -1, + [REFERENCE_PERF] = -1, + [LOWEST_FREQ] = 44, + [NOMINAL_FREQ] = 48, +}; + +typedef struct CpuhzState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t HighestPerformance; + uint32_t NominalPerformance; + uint32_t LowestNonlinearPerformance; + uint32_t LowestPerformance; + uint32_t GuaranteedPerformance; + uint32_t DesiredPerformance; + uint64_t ReferencePerformanceCounter; + uint64_t DeliveredPerformanceCounter; + uint32_t PerformanceLimited; + uint32_t LowestFreq; + uint32_t NominalFreq; + uint32_t reg_size; +} CpuhzState; + + +static uint64_t cpufreq_read(void *opaque, hwaddr offset, unsigned size) +{ + CpuhzState *s = (CpuhzState *)opaque; + uint64_t r; + uint64_t n; + + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int smp_cpus = ms->smp.cpus; + + if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) { + warn_report("cpufreq_read: offset 0x%lx out of range", offset); + return 0; + } + + n = offset % CPPC_REG_PER_CPU_STRIDE; + switch (n) { + case 0: + r = s->HighestPerformance; + break; + case 4: + r = s->NominalPerformance; + break; + case 8: + r = s->LowestNonlinearPerformance; + break; + case 12: + r = s->LowestPerformance; + break; + case 16: + r = s->GuaranteedPerformance; + break; + case 20: + r = s->DesiredPerformance; + break; + /* + * We don't have real counters and it is hard to emulate, so always set the + * counter value to 1 to rely on Linux to use the DesiredPerformance value + * directly. + */ + case 24: + r = s->ReferencePerformanceCounter; + break; + /* + * Guest may still access the register by 32bit; add the process to + * eliminate unnecessary warnings. + */ + case 28: + r = s->ReferencePerformanceCounter >> 32; + break; + case 32: + r = s->DeliveredPerformanceCounter; + break; + case 36: + r = s->DeliveredPerformanceCounter >> 32; + break; + + case 40: + r = s->PerformanceLimited; + break; + case 44: + r = s->LowestFreq; + break; + case 48: + r = s->NominalFreq; + break; + default: + error_printf("cpufreq_read: Bad offset 0x%lx\n", offset); + r = 0; + break; + } + return r; +} + +static void cpufreq_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + uint64_t n; + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int smp_cpus = ms->smp.cpus; + + if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) { + error_printf("cpufreq_write: offset 0x%lx out of range", offset); + return; + } + + n = offset % CPPC_REG_PER_CPU_STRIDE; + + switch (n) { + case 20: + break; + default: + error_printf("cpufreq_write: Bad offset 0x%lx\n", offset); + } +} + +static uint32_t CPPC_Read(const char *hostpath) +{ + int fd; + char buffer[HZ_MAX_LENGTH] = { 0 }; + uint64_t hz; + int len; + const char *endptr = NULL; + int ret; + + fd = qemu_open_old(hostpath, O_RDONLY); + if (fd < 0) { + return 0; + } + + len = read(fd, buffer, HZ_MAX_LENGTH); + qemu_close(fd); + if (len <= 0) { + return 0; + } + ret = qemu_strtoul(buffer, &endptr, 0, &hz); + if (ret < 0) { + return 0; + } + return (uint32_t)hz; +} + +static const MemoryRegionOps cpufreq_ops = { + .read = cpufreq_read, + .write = cpufreq_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void hz_init(CpuhzState *s) +{ + uint32_t hz; + + hz = CPPC_Read(NOMINAL_FREQ_FILE); + if (hz == 0) { + hz = CPPC_Read(CPU_MAX_FREQ_FILE); + if (hz == 0) { + hz = DEFAULT_HZ; + } else { + /* Value in CpuMaxFrequency is in KHz unit; convert to MHz */ + hz = hz / 1000; + } + } + + s->HighestPerformance = hz; + s->NominalPerformance = hz; + s->LowestNonlinearPerformance = hz; + s->LowestPerformance = hz; + s->GuaranteedPerformance = hz; + s->DesiredPerformance = hz; + s->ReferencePerformanceCounter = 1; + s->DeliveredPerformanceCounter = 1; + s->PerformanceLimited = 0; + s->LowestFreq = hz; + s->NominalFreq = hz; +} + +static void cpufreq_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CpuhzState *s = CPUFREQ(obj); + + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int smp_cpus = ms->smp.cpus; + + s->reg_size = smp_cpus * CPPC_REG_PER_CPU_STRIDE; + if (s->reg_size > MAX_SUPPORT_SPACE) { + error_report("Required space 0x%x excesses the max support 0x%x", + s->reg_size, MAX_SUPPORT_SPACE); + goto err_end; + } + + memory_region_init_io(&s->iomem, OBJECT(s), &cpufreq_ops, s, "cpufreq", + s->reg_size); + sysbus_init_mmio(sbd, &s->iomem); + hz_init(s); + return; + +err_end: + /* Set desired perf register offset to -1 to indicate no support for CPPC */ + cppc_regs_offset[DESIRED_PERF] = -1; +} + +static const TypeInfo cpufreq_arm_info = { + .name = TYPE_CPUFREQ, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CpuhzState), + .instance_init = cpufreq_init, +}; + +static void cpufreq_register_types(void) +{ + type_register_static(&cpufreq_arm_info); +} + +type_init(cpufreq_register_types) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index adf6347bc4..448ea6afb4 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -25,6 +25,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c')) acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c')) acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c')) +acpi_ss.add(when: 'CONFIG_CPUFREQ', if_true: files('cpufreq.c')) softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c')) softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c', diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 674f902652..1ca705654b 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -60,7 +60,68 @@ #define ACPI_BUILD_TABLE_SIZE 0x20000 -static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms) +static void acpi_dsdt_add_psd(Aml *dev, int cpus) +{ + Aml *pkg; + Aml *sub; + + sub = aml_package(5); + aml_append(sub, aml_int(5)); + aml_append(sub, aml_int(0)); + /* Assume all vCPUs belong to the same domain */ + aml_append(sub, aml_int(0)); + /* SW_ANY: OSPM coordinate, initiate on any processor */ + aml_append(sub, aml_int(0xFD)); + aml_append(sub, aml_int(cpus)); + + pkg = aml_package(1); + aml_append(pkg, sub); + + aml_append(dev, aml_name_decl("_PSD", pkg)); +} + +static void acpi_dsdt_add_cppc(Aml *dev, uint64_t cpu_base, int *regs_offset) +{ + Aml *cpc; + int i; + + /* Use version 3 of CPPC table from ACPI 6.3 */ + cpc = aml_package(23); + aml_append(cpc, aml_int(23)); + aml_append(cpc, aml_int(3)); + + for (i = 0; i < CPPC_REG_COUNT; i++) { + Aml *res; + uint8_t reg_width; + uint8_t acc_type; + uint64_t addr; + + if (regs_offset[i] == -1) { + reg_width = 0; + acc_type = AML_ANY_ACC; + addr = 0; + } else { + addr = cpu_base + regs_offset[i]; + if (i == REFERENCE_CTR || i == DELIVERED_CTR) { + reg_width = 64; + acc_type = AML_QWORD_ACC; + } else { + reg_width = 32; + acc_type = AML_DWORD_ACC; + } + } + + res = aml_resource_template(); + aml_append(res, aml_generic_register(AML_SYSTEM_MEMORY, reg_width, 0, + acc_type, addr)); + aml_append(cpc, res); + } + + aml_append(dev, aml_name_decl("_CPC", cpc)); +} + +static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms, + const MemMapEntry *cppc_memmap) { MachineState *ms = MACHINE(vms); uint16_t i; @@ -69,6 +130,18 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms) Aml *dev = aml_device("C%.03X", i); aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007"))); aml_append(dev, aml_name_decl("_UID", aml_int(i))); + + /* + * Append _CPC and _PSD to support CPU frequence show + * Check CPPC available by DESIRED_PERF register + */ + if (cppc_regs_offset[DESIRED_PERF] != -1) { + acpi_dsdt_add_cppc(dev, + cppc_memmap->base + i * CPPC_REG_PER_CPU_STRIDE, + cppc_regs_offset); + acpi_dsdt_add_psd(dev, ms->smp.cpus); + } + aml_append(scope, dev); } } @@ -858,7 +931,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * the RTC ACPI device at all when using UEFI. */ scope = aml_scope("\\_SB"); - acpi_dsdt_add_cpus(scope, vms); + acpi_dsdt_add_cpus(scope, vms, &memmap[VIRT_CPUFREQ]); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); if (vmc->acpi_expose_flash) { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 529c0d38b6..0538d258fa 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -154,6 +154,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + [VIRT_CPUFREQ] = { 0x0b000000, 0x00010000 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 }, @@ -931,6 +932,16 @@ static void create_uart(const VirtMachineState *vms, int uart, g_free(nodename); } +static void create_cpufreq(const VirtMachineState *vms, MemoryRegion *mem) +{ + hwaddr base = vms->memmap[VIRT_CPUFREQ].base; + DeviceState *dev = qdev_new("cpufreq"); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + + sysbus_realize_and_unref(s, &error_fatal); + memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); +} + static void create_rtc(const VirtMachineState *vms) { char *nodename; @@ -2190,6 +2201,8 @@ static void machvirt_init(MachineState *machine) create_uart(vms, VIRT_UART, sysmem, serial_hd(0)); + create_cpufreq(vms, sysmem); + if (vms->secure) { create_secure_ram(vms, secure_sysmem, secure_tag_sysmem); create_uart(vms, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 6b6cf2fc1d..335a60c2c1 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -71,3 +71,7 @@ config GOLDFISH_TTY config SHAKTI_UART bool + +config CPUFREQ + bool + default y diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index c97e8633ad..ab86583228 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -92,4 +92,42 @@ typedef struct AcpiFadtData { #define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0) #define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1) +/* + * CPPC register definition from kernel header + * include/acpi/cppc_acpi.h + * The last element is newly added for easy use + */ +enum cppc_regs { + HIGHEST_PERF, + NOMINAL_PERF, + LOW_NON_LINEAR_PERF, + LOWEST_PERF, + GUARANTEED_PERF, + DESIRED_PERF, + MIN_PERF, + MAX_PERF, + PERF_REDUC_TOLERANCE, + TIME_WINDOW, + CTR_WRAP_TIME, + REFERENCE_CTR, + DELIVERED_CTR, + PERF_LIMITED, + ENABLE, + AUTO_SEL_ENABLE, + AUTO_ACT_WINDOW, + ENERGY_PERF, + REFERENCE_PERF, + LOWEST_FREQ, + NOMINAL_FREQ, + CPPC_REG_COUNT, +}; + +#define CPPC_REG_PER_CPU_STRIDE 0x40 + +/* + * Offset for each CPPC register; -1 for unavailable + * The whole register space is unavailable if desired perf offset is -1. + */ +extern int cppc_regs_offset[CPPC_REG_COUNT]; + #endif diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 8e8ad8029e..2e00d2e208 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -429,6 +429,9 @@ Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, uint8_t channel); Aml *aml_sleep(uint64_t msec); Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source); +Aml *aml_generic_register(AmlRegionSpace rs, uint8_t reg_width, + uint8_t reg_offset, AmlAccessType type, + uint64_t addr); /* Block AML object primitives */ Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index dc6b66ffc8..a4356cf736 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -70,6 +70,7 @@ enum { VIRT_GIC_REDIST, VIRT_SMMU, VIRT_UART, + VIRT_CPUFREQ, VIRT_MMIO, VIRT_RTC, VIRT_FW_CFG, diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT index c47503990715d389914fdf9c8bccb510761741ac..dd8573f0312918ea1ba17496e9f3a275e98ab214 100644 GIT binary patch delta 514 zcmX@3u~di4CDC{9nX`Y4z#+dkEr}*e5XZeW+lJy+%&H>Is4l?3g;#|yv zB3yz^JPZs949pA+4BSA>P|1KK$bwCf1Dhbw5KQd?n1b~T%pw?~AbX(zVLeO)NK;MF zfuWv70>gR-1{MVjL12(*Uo4O*Pi7GSAkZ%L delta 38 tcmZ3gb4G*9CD#+dkEr}*e5sfmV?o0XWDMF7oq3Hks4 diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp index bae36cdd397473afe3923c52f030641a5ab19d5d..d764481440adea59d928a1a48afe0ba1ce135597 100644 GIT binary patch delta 514 zcmbPh{LPHZCDGeb|X`aTdj4|=SPVv!A&hirtBFt7`TC$p^^bfkOi9{2R1>VA(+|)Fa_%wm_;x|LH0rc!g`npkfxfT z14BKF1cvnv3@i#5g1{irz!0R|P0=tT2>1zTy$*);KtThzAV+*au!|56qYx)67b^n; L*I&kso*|L|A3`us delta 38 ucmexnHrJTTCD8Drvuo#LaLq$V0lZdPInkpuwgSPN7D diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem index c47503990715d389914fdf9c8bccb510761741ac..dd8573f0312918ea1ba17496e9f3a275e98ab214 100644 GIT binary patch delta 514 zcmX@3u~di4CDC{9nX`Y4z#+dkEr}*e5XZeW+lJy+%&H>Is4l?3g;#|yv zB3yz^JPZs949pA+4BSA>P|1KK$bwCf1Dhbw5KQd?n1b~T%pw?~AbX(zVLeO)NK;MF zfuWv70>gR-1{MVjL12(*Uo4O*Pi7GSAkZ%L delta 38 tcmZ3gb4G*9CD#+dkEr}*e5sfmV?o0XWDMF7oq3Hks4 diff --git a/tests/data/acpi/virt/DSDT.pxb b/tests/data/acpi/virt/DSDT.pxb index fbd78f44c4785d19759daea909fe6d6f9a6e6b01..9ff22b5ea465d2f678beb2ce9d905861d69a5b87 100644 GIT binary patch delta 514 zcmexweZ!v1CD6}CMj=jCE>;Ex MuD^^MJ-5jM0391Kw*UYD delta 38 ucmca%|KFO+CD Date: Mon, 29 Jul 2019 16:16:35 +0800 Subject: [PATCH 061/486] pl011: reset read FIFO when UARTTIMSC=0 & UARTICR=0xffff We can enable ACPI when AArch64 Linux is booted with QEMU and UEFI (AAVMF). When VM is booting and the SBSA driver has not initialized, writting data that exceds 32 bytes will cause the read FIFO full and proceeding data will be lost. The searil port appears to be stuck in this abnormal situation. A hack to reset read FIFO when UARTTIMSC=0 & UARTICR=0xffff appears to resolve the issue. The question is fully discussed at https://www.spinics.net/lists/linux-serial/msg23163.html Signed-off-by: Haibin Wang Reviewed-by: Shannon Zhao Reviewed-by: Ying Fang Signed-off-by: Yan Wang --- hw/char/pl011.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 6e2d7f7509..8ca2a4ed68 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -255,6 +255,10 @@ static void pl011_write(void *opaque, hwaddr offset, case 17: /* UARTICR */ s->int_level &= ~value; pl011_update(s); + if (!s->int_enabled && !s->int_level) { + s->read_count = 0; + s->read_pos = 0; + } break; case 18: /* UARTDMACR */ s->dmacr = value; -- Gitee From a999e010c6af90f0fc1ad9b998e2a9b760c40f1a Mon Sep 17 00:00:00 2001 From: zhanghailiang Date: Thu, 25 Jul 2019 16:05:11 +0800 Subject: [PATCH 062/486] qcow2: fix memory leak in qcow2_read_extensions Free feature_table if it is failed in bdrv_pread. Signed-off-by: fangyi Signed-off-by: Yan Wang --- block/qcow2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/qcow2.c b/block/qcow2.c index d509016756..be90a898e3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -272,6 +272,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ret = bdrv_pread(bs->file, offset , feature_table, ext.len); if (ret < 0) { + g_free(feature_table); error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " "Could not read table"); return ret; -- Gitee From e026850b32231abb97d7790a04d7c94515bd1081 Mon Sep 17 00:00:00 2001 From: Pan Nengyuan Date: Mon, 13 Jan 2020 15:53:32 +0800 Subject: [PATCH 063/486] scsi-disk: define props in scsi_block_disk to avoid memleaks scsi_block_realize() use scsi_realize() to init some props, but these props is not defined in scsi_block_disk_properties, so they will not be freed. This patch defines these prop in scsi_block_disk_properties to avoid memleaks. Signed-off-by: Pan Nengyuan Signed-off-by: Yan Wang --- hw/scsi/scsi-disk.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index d4914178ea..1d7799de93 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3107,9 +3107,7 @@ static const TypeInfo scsi_cd_info = { #ifdef __linux__ static Property scsi_block_properties[] = { - DEFINE_BLOCK_ERROR_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), - DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), + DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, DEFAULT_MAX_UNMAP_SIZE), -- Gitee From f8cfcb7e3e43dae4a0d8d81f77d425339022f801 Mon Sep 17 00:00:00 2001 From: Qiang Ning Date: Mon, 12 Jul 2021 17:30:45 +0800 Subject: [PATCH 064/486] hw/net/rocker_of_dpa: fix double free bug of rocker device The of_dpa_cmd_add_l2_flood function of the rocker device releases the memory of group->l2_flood.group_ids before applying for new memory. If the l2_group configured by the guest does not match the input group->l2_flood.group_ids, the err_out branch is redirected to release the memory of the group->l2_flood.group_ids branch. The pointer is not set to NULL after the memory is freed. When the guest accesses the of_dpa_cmd_add_l2_flood function again, the memory of group->l2_flood.group_ids is released again. As a result, the memory is double free. Fix that by setting group->l2_flood.group_ids to NULL after free. Signed-off-by: Jiajie Li Signed-off-by: Qiang Ning Signed-off-by: Yan Wang --- hw/net/rocker/rocker_of_dpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index b3b8c5bb6d..8ac26e6beb 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -2070,6 +2070,7 @@ static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group, err_out: group->l2_flood.group_count = 0; g_free(group->l2_flood.group_ids); + group->l2_flood.group_ids = NULL; g_free(tlvs); return err; -- Gitee From 2412c1968777a0fe77cb24dda935e3414e00ebb1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 8 Feb 2022 16:10:31 +0800 Subject: [PATCH 065/486] pcie: Add pcie-root-port fast plug/unplug feature If a device is plugged in the pcie-root-port when VM kernel is booting, the kernel may wrongly disable the device. This bug was brought in by two patches of the linux kernel: https://patchwork.kernel.org/patch/10575355/ https://patchwork.kernel.org/patch/10766219/ VM runtime like kata uses this feature to boot microVM, so we must fix it up. We hack into the pcie native hotplug patch so that hotplug/unplug will work under this circumstance. Signed-off-by: Ying Fang Signed-off-by: Yan Wang --- hw/core/machine.c | 2 ++ hw/pci-bridge/gen_pcie_root_port.c | 2 ++ hw/pci/pcie.c | 13 ++++++++++++- include/hw/pci/pcie_port.h | 3 +++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 53a99abc56..126e3e25ce 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -121,6 +121,8 @@ const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0); GlobalProperty hw_compat_3_1[] = { { "pcie-root-port", "x-speed", "2_5" }, { "pcie-root-port", "x-width", "1" }, + { "pcie-root-port", "fast-plug", "0" }, + { "pcie-root-port", "fast-unplug", "0" }, { "memory-backend-file", "x-use-canonical-path-for-ramblock-id", "true" }, { "memory-backend-memfd", "x-use-canonical-path-for-ramblock-id", "true" }, { "tpm-crb", "ppi", "false" }, diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 20099a8ae3..0bf9df9c58 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -140,6 +140,8 @@ static Property gen_rp_props[] = { speed, PCIE_LINK_SPEED_16), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, width, PCIE_LINK_WIDTH_32), + DEFINE_PROP_UINT8("fast-plug", PCIESlot, fast_plug, 0), + DEFINE_PROP_UINT8("fast-unplug", PCIESlot, fast_unplug, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index d7d73a31e4..d7d1504bcb 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -526,6 +526,7 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, uint8_t *exp_cap = hotplug_pdev->config + hotplug_pdev->exp.exp_cap; uint32_t sltcap = pci_get_word(exp_cap + PCI_EXP_SLTCAP); uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); + PCIESlot *s = PCIE_SLOT(hotplug_pdev); /* Check if hot-unplug is disabled on the slot */ if ((sltcap & PCI_EXP_SLTCAP_HPC) == 0) { @@ -572,7 +573,17 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, return; } - pcie_cap_slot_push_attention_button(hotplug_pdev); + if ((pci_dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) && s->fast_plug) { + pci_word_test_and_clear_mask(pci_dev->config + pci_dev->exp.exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA); + } + + if (s->fast_unplug) { + pcie_cap_slot_event(hotplug_pdev, + PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); + } else { + pcie_cap_slot_push_attention_button(hotplug_pdev); + } } /* pci express slot for pci express root/downstream port diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index e25b289ce8..5b80a13c4d 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -51,6 +51,9 @@ struct PCIESlot { uint8_t chassis; uint16_t slot; + uint8_t fast_plug; + uint8_t fast_unplug; + PCIExpLinkSpeed speed; PCIExpLinkWidth width; -- Gitee From 14d1ad1309a1bd035250512368221088c2f83f32 Mon Sep 17 00:00:00 2001 From: fangying Date: Wed, 18 Mar 2020 12:51:33 +0800 Subject: [PATCH 066/486] pcie: Compat with devices which do not support Link Width, such as ioh3420 We hack into PCI_EXP_LNKCAP to support device fast plug/unplug for pcie-root-port. However some devices like ioh3420 does not suport it, so PCI_EXP_LNKCAP is not set for such devices. Signed-off-by: Ying Fang Signed-off-by: Yan Wang --- hw/pci/pcie.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index d7d1504bcb..30c09ed943 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -92,13 +92,6 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev) return; } - /* Clear and fill LNKCAP from what was configured above */ - pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP, - PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); - pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, - QEMU_PCI_EXP_LNKCAP_MLW(s->width) | - QEMU_PCI_EXP_LNKCAP_MLS(s->speed)); - /* * Link bandwidth notification is required for all root ports and * downstream ports supporting links wider than x1 or multiple link @@ -106,6 +99,12 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev) */ if (s->width > QEMU_PCI_EXP_LNK_X1 || s->speed > QEMU_PCI_EXP_LNK_2_5GT) { + /* Clear and fill LNKCAP from what was configured above */ + pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP, + PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, + QEMU_PCI_EXP_LNKCAP_MLW(s->width) | + QEMU_PCI_EXP_LNKCAP_MLS(s->speed)); pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, PCI_EXP_LNKCAP_LBNC); } -- Gitee From 4b156248776f734d63fe37629d56c40234fda9c0 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 10:42:33 +0800 Subject: [PATCH 067/486] nbd/server.c: fix invalid read after client was already free In the process of NBD equipment pressurization, executing QEMU NBD will lead to the failure of IO distribution and go to NBD_ Out process of trip(). If two or more IO go to the out process, client NBD will release in nbd_request_put(). The user after free problem that is read again in close(). Through the NBD_ Save the value of client > closing before the out process in trip to solve the use after free problem. Signed-off-by: wangjian161 --- nbd/server.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nbd/server.c b/nbd/server.c index 4630dd7322..37515ed520 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2606,6 +2606,7 @@ static coroutine_fn void nbd_trip(void *opaque) NBDRequestData *req; NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ int ret; + bool client_closing; Error *local_err = NULL; trace_nbd_trip(); @@ -2681,8 +2682,11 @@ disconnect: if (local_err) { error_reportf_err(local_err, "Disconnect client, due to: "); } + client_closing = client->closing; nbd_request_put(req); - client_close(client, true); + if (!client_closing) { + client_close(client, true); + } nbd_client_put(client); } -- Gitee From de6f3fb0cf92e04c0989a9065910158eecbe4304 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 10:48:58 +0800 Subject: [PATCH 068/486] qemu-nbd: make native as the default aio mode When the file system is dealing with multithreading concurrent writing to a file, the performance will be degraded because of the lock. At present, the default AIO mode of QEMU NBD is threads. In the case of large blocks, because IO is divided into small pieces and multiple queues, it will become multithreading concurrent writing the same file. Due to the file system, the performance will be greatly reduced. If you change to native mode, this problem will not exist. Signed-off-by: wangjian161 --- qemu-nbd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qemu-nbd.c b/qemu-nbd.c index c6c20df68a..15a4bc4018 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -800,6 +800,10 @@ int main(int argc, char **argv) trace_init_file(); qemu_set_log(LOG_TRACE); + if (!seen_aio && (flags & BDRV_O_NOCACHE)) { + flags |= BDRV_O_NATIVE_AIO; + } + socket_activation = check_socket_activation(); if (socket_activation == 0) { setup_address_and_port(&bindto, &port); -- Gitee From f665f7836a019cc8bb8d46d076508afc761923f0 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 10:55:08 +0800 Subject: [PATCH 069/486] qemu-nbd: set timeout to qemu-nbd socket In case of insufficient memory and kill-9, the NBD socket cannot be processed and stuck all the time. Signed-off-by: wangjian161 --- nbd/client.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nbd/client.c b/nbd/client.c index 30d5383cb1..8ed50140f2 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -24,6 +24,8 @@ #include "nbd-internal.h" #include "qemu/cutils.h" +#define NBD_TIMEOUT_SECONDS 30 + /* Definitions for opaque data types */ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); @@ -1301,6 +1303,12 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, } } + if (ioctl(fd, NBD_SET_TIMEOUT, NBD_TIMEOUT_SECONDS) < 0) { + int serrno = errno; + error_setg(errp, "Failed setting timeout"); + return -serrno; + } + trace_nbd_init_finish(); return 0; -- Gitee From 56f59125707c0222bbb5d7f820792aba17c3db08 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 11:10:42 +0800 Subject: [PATCH 070/486] qemu-pr: fixed ioctl failed for multipath disk We use ioctl to detect multipath devices. However, we only set flags in struct dm_ioctl (the argument to ioctl) and left other fields in random, which may cause the failure of calling ioctl. Hence, we set other fields to 0 to avoid the failure. Signed-off-by: wangjian161 --- scsi/qemu-pr-helper.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index f281daeced..bbb9b57741 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -288,9 +288,12 @@ static void multipath_pr_init(void) static int is_mpath(int fd) { - struct dm_ioctl dm = { .flags = DM_NOFLUSH_FLAG }; + struct dm_ioctl dm; struct dm_target_spec *tgt; + memset(&dm, 0, sizeof(struct dm_ioctl)); + dm.flags = DM_NOFLUSH_FLAG; + tgt = dm_dev_ioctl(fd, DM_TABLE_STATUS, &dm); if (!tgt) { if (errno == ENXIO) { -- Gitee From 21b172a3ce13c3b499e4265628f7d7c7e1189749 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 11:18:21 +0800 Subject: [PATCH 071/486] block: enable cache mode of empty cdrom enable cache mode even if cdrom is empty Signed-off-by: wangjian161 --- blockdev.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/blockdev.c b/blockdev.c index 10a73fa423..37e3ee6f26 100644 --- a/blockdev.c +++ b/blockdev.c @@ -492,6 +492,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, QDict *interval_dict = NULL; QList *interval_list = NULL; const char *id; + const char *cache; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; const char *throttling_group = NULL; @@ -583,6 +584,21 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, read_only = qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false); + if (!file || !*file) { + cache = qdict_get_try_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH); + if (cache && !strcmp(cache, "on")) { + bdrv_flags |= BDRV_O_NO_FLUSH; + } + + cache = qdict_get_try_str(bs_opts, BDRV_OPT_CACHE_DIRECT); + if (cache && !strcmp(cache, "on")) { + bdrv_flags |= BDRV_O_NOCACHE; + } + + qdict_del(bs_opts, BDRV_OPT_CACHE_NO_FLUSH); + qdict_del(bs_opts, BDRV_OPT_CACHE_DIRECT); + } + /* init */ if ((!file || !*file) && !qdict_size(bs_opts)) { BlockBackendRootState *blk_rs; -- Gitee From 0a2c96ee5a3463e82397afb9cb36f340a93264c2 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 11:29:15 +0800 Subject: [PATCH 072/486] block: disallow block jobs when there is a BDRV_O_INACTIVE flag Currently, migration will put a BDRV_O_INACTIVE flag on bs's open_flags until another resume being called. In that case, any IO from vm or block jobs will cause a qemu crash with an assert 'assert(!(bs->open_flags & BDRV_O_INACTIVE))' failure in bdrv_co_pwritev function. we hereby disallow block jobs by faking a blocker. Signed-off-by: wangjian161 --- block.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/block.c b/block.c index 0ac5b163d2..26c3982567 100644 --- a/block.c +++ b/block.c @@ -6692,6 +6692,22 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) bdrv_get_device_or_node_name(bs)); return true; } + + /* + * When migration puts a BDRV_O_INACTIVE flag on driver's open_flags, + * we fake a blocker that doesn't exist. From now on, block jobs + * will not be permitted. + */ + if ((op == BLOCK_OP_TYPE_RESIZE || op == BLOCK_OP_TYPE_COMMIT_SOURCE || + op == BLOCK_OP_TYPE_MIRROR_SOURCE || op == BLOCK_OP_TYPE_MIRROR_TARGET) && + (bs->open_flags & BDRV_O_INACTIVE)) { + if (errp) { + error_setg(errp, "block device is in use by migration with" + " a driver BDRV_O_INACTIVE flag setted"); + } + return true; + } + return false; } -- Gitee From 77496578b22e127eb50a5a8c463e92fb3245a7e0 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 11:42:47 +0800 Subject: [PATCH 073/486] scsi: cdrom: Fix crash after remote cdrom detached There is a small window between the twice blk_is_available in scsi_disk_emulate_command which would cause crash due to the later assertion if the remote cdrom is detached in this window. So this patch replaces assertions with return to avoid qemu crash. Signed-off-by: wangjian161 --- hw/scsi/scsi-disk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index d4914178ea..a1053f4036 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1930,7 +1930,10 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) memset(outbuf, 0, r->buflen); switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - assert(blk_is_available(s->qdev.conf.blk)); + if (!blk_is_available(s->qdev.conf.blk)) { + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + return 0; + } break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); -- Gitee From 87d8b7dcd880e0cef0c043dfef5ae649652cfe21 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 11:51:43 +0800 Subject: [PATCH 074/486] block: bugfix: disable process AIO when attach scsi disk When initializing the virtio-scsi disk, hd_geometry_guess() will be called to process AIO. At this time, the scsi disk has not been fully initialized, and some fields in struct SCSIDiskState, such as vendor and version, are NULL. If processing AIO at this time, qemu may crash down. Add aio_disable_external() before hd_geometry_guess() to disable processing AIO at that time. Signed-off-by: wangjian161 --- hw/block/block.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/block/block.c b/hw/block/block.c index 26c0767552..2cfc93a68e 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -224,9 +224,16 @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, Error **errp) { if (!conf->cyls && !conf->heads && !conf->secs) { + AioContext *ctx = blk_get_aio_context(conf->blk); + + /* Callers may not expect this function to dispatch aio handlers, so + * disable external aio such as guest device emulation. + */ + aio_disable_external(ctx); hd_geometry_guess(conf->blk, &conf->cyls, &conf->heads, &conf->secs, ptrans); + aio_enable_external(ctx); } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); } -- Gitee From d0586db311e8b78732923ce46f149fdf8251a59c Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 16:10:22 +0800 Subject: [PATCH 075/486] block: bugfix: Don't pause vm when NOSPACE EIO happened When backend disk is FULL and disk IO type is 'dataplane', QEMU will pause the vm, and this may cause endless-loop in QEMU main thread if we do the snapshot merge now. When backend disk is FULL, only reporting an error rather than pausing the virtual machine. Signed-off-by: wangjian161 --- blockdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 37e3ee6f26..3ce294ec4a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -556,7 +556,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, qdict_put_str(bs_opts, "driver", buf); } - on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; + on_write_error = BLOCKDEV_ON_ERROR_REPORT; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { on_write_error = parse_block_error_action(buf, 0, &error); if (error) { -- Gitee From ba8fd8a3d11655da0b51148e69c01b78794a3f69 Mon Sep 17 00:00:00 2001 From: WangJian Date: Wed, 9 Feb 2022 16:34:05 +0800 Subject: [PATCH 076/486] scsi: bugfix: fix division by zero Error of PRDM disk may cause divide by zero in scsi_read_complete(), so add LOG and assert(). Signed-off-by: wangjian161 --- hw/scsi/scsi-generic.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 0306ccc7b1..1f51586048 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -179,6 +179,10 @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) (r->req.cmd.buf[1] & 0x01)) { page = r->req.cmd.buf[2]; if (page == 0xb0) { + if (s->blocksize == 0) { + qemu_log("device blocksize is 0!\n"); + abort(); + } uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk); uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk); @@ -314,11 +318,23 @@ static void scsi_read_complete(void * opaque, int ret) /* Snoop READ CAPACITY output to set the blocksize. */ if (r->req.cmd.buf[0] == READ_CAPACITY_10 && (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) { - s->blocksize = ldl_be_p(&r->buf[4]); + int new_blocksize = ldl_be_p(&r->buf[4]); + if (s->blocksize != new_blocksize) { + qemu_log("device id=%s type=%d: blocksize %d change to %d\n", + s->qdev.id ? s->qdev.id : "null", s->type, + s->blocksize, new_blocksize); + } + s->blocksize = new_blocksize; s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL; } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - s->blocksize = ldl_be_p(&r->buf[8]); + int new_blocksize = ldl_be_p(&r->buf[8]); + if (s->blocksize != new_blocksize) { + qemu_log("device id=%s type=%d: blocksize %d change to %d\n", + s->qdev.id ? s->qdev.id : "null", s->type, + s->blocksize, new_blocksize); + } + s->blocksize = new_blocksize; s->max_lba = ldq_be_p(&r->buf[0]); } blk_set_guest_block_size(s->conf.blk, s->blocksize); -- Gitee From f329ec9bd971ba7776cadb57e7311bfb6da41060 Mon Sep 17 00:00:00 2001 From: Jiahui Cen Date: Thu, 18 Mar 2021 19:45:11 +0800 Subject: [PATCH 077/486] block: Add sanity check when setting retry parameters Add sanity check when setting retry parameters to avoid invalid retry configuration. Signed-off-by: Jiahui Cen Signed-off-by: Alex Chen --- hw/core/qdev-prop-internal.h | 2 ++ hw/core/qdev-properties-system.c | 45 +++++++++++++++++++++++++++++ hw/core/qdev-properties.c | 4 +-- include/hw/block/block.h | 7 +++-- include/hw/qdev-properties-system.h | 8 +++++ 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/hw/core/qdev-prop-internal.h b/hw/core/qdev-prop-internal.h index d7b77844fe..68b1b9d10c 100644 --- a/hw/core/qdev-prop-internal.h +++ b/hw/core/qdev-prop-internal.h @@ -22,6 +22,8 @@ void qdev_propinfo_set_default_value_uint(ObjectProperty *op, void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp); +void qdev_propinfo_get_int64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp); void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp); diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 6a6ff03be7..b93ed9b4dd 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -612,6 +612,51 @@ const PropertyInfo qdev_prop_blockdev_on_error = { .set_default_value = qdev_propinfo_set_default_value_enum, }; +static void set_retry_time(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + int64_t value, *ptr = object_field_prop_ptr(obj, prop); + Error *local_err = NULL; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_int64(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* value should not be negative */ + if (value < 0) { + error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, + dev->id ? : "", name, (int64_t)value, 0L, LONG_MAX); + return; + } + + *ptr = value; +} + +const PropertyInfo qdev_prop_blockdev_retry_interval = { + .name = "BlockdevRetryInterval", + .description = "Interval for retry error handling policy", + .get = qdev_propinfo_get_int64, + .set = set_retry_time, + .set_default_value = qdev_propinfo_set_default_value_int, +}; + +const PropertyInfo qdev_prop_blockdev_retry_timeout = { + .name = "BlockdevRetryTimeout", + .description = "Timeout for retry error handling policy", + .get = qdev_propinfo_get_int64, + .set = set_retry_time, + .set_default_value = qdev_propinfo_set_default_value_int, +}; + /* --- BIOS CHS translation */ QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index c34aac6ebc..2d5f662663 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -396,7 +396,7 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, ptr, errp); } -static void get_int64(Object *obj, Visitor *v, const char *name, +void qdev_propinfo_get_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { Property *prop = opaque; @@ -423,7 +423,7 @@ const PropertyInfo qdev_prop_uint64 = { const PropertyInfo qdev_prop_int64 = { .name = "int64", - .get = get_int64, + .get = qdev_propinfo_get_int64, .set = set_int64, .set_default_value = qdev_propinfo_set_default_value_int, }; diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 24fb7d77af..282929e8f0 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -82,9 +82,10 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) BLOCKDEV_ON_ERROR_AUTO), \ DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \ BLOCKDEV_ON_ERROR_AUTO), \ - DEFINE_PROP_INT64("retry_interval", _state, _conf.retry_interval, \ - -1), \ - DEFINE_PROP_INT64("retry_timeout", _state, _conf.retry_timeout, -1) + DEFINE_PROP_BLOCKDEV_RETRY_INTERVAL("retry_interval", _state, \ + _conf.retry_interval, 1000), \ + DEFINE_PROP_BLOCKDEV_RETRY_TIMEOUT("retry_timeout", _state, \ + _conf.retry_timeout, 0) /* Backend access helpers */ diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index 0ac327ae60..906a027676 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -9,6 +9,8 @@ extern const PropertyInfo qdev_prop_reserved_region; extern const PropertyInfo qdev_prop_multifd_compression; extern const PropertyInfo qdev_prop_losttickpolicy; extern const PropertyInfo qdev_prop_blockdev_on_error; +extern const PropertyInfo qdev_prop_blockdev_retry_interval; +extern const PropertyInfo qdev_prop_blockdev_retry_timeout; extern const PropertyInfo qdev_prop_bios_chs_trans; extern const PropertyInfo qdev_prop_fdc_drive_type; extern const PropertyInfo qdev_prop_drive; @@ -47,6 +49,12 @@ extern const PropertyInfo qdev_prop_pcie_link_width; #define DEFINE_PROP_BLOCKDEV_ON_ERROR(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_blockdev_on_error, \ BlockdevOnError) +#define DEFINE_PROP_BLOCKDEV_RETRY_INTERVAL(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_blockdev_retry_interval, \ + int64_t) +#define DEFINE_PROP_BLOCKDEV_RETRY_TIMEOUT(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_blockdev_retry_timeout, \ + int64_t) #define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int) #define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \ -- Gitee From 9a8de722b047ba66f70e87fb29b877935c187457 Mon Sep 17 00:00:00 2001 From: Lichang Zhao Date: Thu, 10 Feb 2022 16:54:06 +0800 Subject: [PATCH 078/486] hw/net/rocker: fix security vulnerability fix security vulnerability Signed-off-by: Lichang zhao Signed-off-by: Jinhao Gao --- hw/net/rocker/rocker_of_dpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index b3b8c5bb6d..8ac26e6beb 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -2070,6 +2070,7 @@ static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group, err_out: group->l2_flood.group_count = 0; g_free(group->l2_flood.group_ids); + group->l2_flood.group_ids = NULL; g_free(tlvs); return err; -- Gitee From 4f09a1c2aa855aab666d729defce4c7f0466cb77 Mon Sep 17 00:00:00 2001 From: Ying Fang Date: Thu, 10 Feb 2022 17:16:55 +0800 Subject: [PATCH 079/486] tests: Disable filemonitor testcase Since filemonitor testcase requires that host kernel being a LTS version, we cannot guarantee that on OBS system. Lets disable it by default. Signed-off-by: Ying Fang Signed-off-by: Jinhao Gao --- tests/unit/meson.build | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/unit/meson.build b/tests/unit/meson.build index acac3622ed..c21d817874 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -129,9 +129,6 @@ if have_system 'test-vmstate': [migration, io], 'test-yank': ['socket-helpers.c', qom, io, chardev] } - if 'CONFIG_INOTIFY1' in config_host - tests += {'test-util-filemonitor': []} - endif # Some tests: test-char, test-qdev-global-props, and test-qga, # are not runnable under TSan due to a known issue. -- Gitee From 124d427a1fdae2d1eeed433093ec4ab78b81237e Mon Sep 17 00:00:00 2001 From: "shenghualong@huawei.com" Date: Thu, 10 Feb 2022 11:11:37 +0800 Subject: [PATCH 080/486] freeclock: add qmp command to get time offset of vm in seconds When setting the system time in VM, a RTC_CHANGE event will be reported. However, if libvirt is restarted while the event is be reporting, the event will be lost and we will get the old time (not the time we set in VM) after rebooting the VM. We save the delta time in QEMU and add a rtc-date-diff qmp to get the delta time so that libvirt can get the latest time in VM according to the qmp after libvirt is restarted. Signed-off-by: Peng Liang Signed-off-by: zhangxinhao --- include/qemu-common.h | 4 +++- monitor/qmp-cmds.c | 5 +++++ qapi/misc.json | 9 +++++++++ qapi/pragma.json | 3 ++- softmmu/rtc.c | 13 ++++++++++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/qemu-common.h b/include/qemu-common.h index 73bcf763ed..9ed8832152 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -27,7 +27,9 @@ int qemu_main(int argc, char **argv, char **envp); #endif void qemu_get_timedate(struct tm *tm, int offset); -int qemu_timedate_diff(struct tm *tm); +time_t qemu_timedate_diff(struct tm *tm); +time_t get_rtc_date_diff(void); +void set_rtc_date_diff(time_t diff); void *qemu_oom_check(void *ptr); diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 343353e27a..98868cee03 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -466,3 +466,8 @@ HumanReadableText *qmp_x_query_irq(Error **errp) return human_readable_text_from_str(buf); } + +int64_t qmp_query_rtc_date_diff(Error **errp) +{ + return get_rtc_date_diff(); +} diff --git a/qapi/misc.json b/qapi/misc.json index 358548abe1..5b6d653682 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -527,3 +527,12 @@ 'data': { '*option': 'str' }, 'returns': ['CommandLineOptionInfo'], 'allow-preconfig': true } + +## +# @query-rtc-date-diff: +# +# get vm's time offset +# +# Since: 2.8 +## +{ 'command': 'query-rtc-date-diff', 'returns': 'int64' } diff --git a/qapi/pragma.json b/qapi/pragma.json index 3bc0335d1f..b37f6de445 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -26,7 +26,8 @@ 'qom-get', 'query-tpm-models', 'query-tpm-types', - 'ringbuf-read' ], + 'ringbuf-read', + 'query-rtc-date-diff' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status diff --git a/softmmu/rtc.c b/softmmu/rtc.c index 5632684fc9..57bb8bba7c 100644 --- a/softmmu/rtc.c +++ b/softmmu/rtc.c @@ -43,6 +43,7 @@ static time_t rtc_ref_start_datetime; static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */ static int rtc_host_datetime_offset = -1; /* valid & used only with RTC_BASE_DATETIME */ +static time_t rtc_date_diff = 0; QEMUClockType rtc_clock; /***********************************************************/ /* RTC reference time/date access */ @@ -84,7 +85,7 @@ void qemu_get_timedate(struct tm *tm, int offset) } } -int qemu_timedate_diff(struct tm *tm) +time_t qemu_timedate_diff(struct tm *tm) { time_t seconds; @@ -107,6 +108,16 @@ int qemu_timedate_diff(struct tm *tm) return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST); } +time_t get_rtc_date_diff(void) +{ + return rtc_date_diff; +} + +void set_rtc_date_diff(time_t diff) +{ + rtc_date_diff = diff; +} + static void configure_rtc_base_datetime(const char *startdate) { time_t rtc_start_datetime; -- Gitee From 1e6bae1d13302594b6e63d88e8627fa477966cf4 Mon Sep 17 00:00:00 2001 From: "shenghualong@huawei.com" Date: Thu, 10 Feb 2022 14:23:28 +0800 Subject: [PATCH 081/486] freeclock: set rtc_date_diff for arm Set rtc_date_diff in pl031. Signed-off-by: Peng Liang Signed-off-by: zhangxinhao --- hw/rtc/pl031.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index e7ced90b02..da8b061e91 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -143,7 +143,8 @@ static void pl031_write(void * opaque, hwaddr offset, s->tick_offset += value - pl031_get_count(s); qemu_get_timedate(&tm, s->tick_offset); - qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); + set_rtc_date_diff(qemu_timedate_diff(&tm)); + qapi_event_send_rtc_change(get_rtc_date_diff()); pl031_set_alarm(s); break; -- Gitee From 3d0846d864384be3d08a54ca6e2ce247a5cee952 Mon Sep 17 00:00:00 2001 From: l00500761 Date: Thu, 10 Feb 2022 14:25:30 +0800 Subject: [PATCH 082/486] freeclock: set rtc_date_diff for X86 Set rtc_date_diff in mc146818rtc. Signed-off-by: l00500761 Signed-off-by: zhangxinhao --- hw/rtc/mc146818rtc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 4fbafddb22..af1df9aaeb 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -616,7 +616,8 @@ static void rtc_set_time(RTCState *s) s->base_rtc = mktimegm(&tm); s->last_update = qemu_clock_get_ns(rtc_clock); - qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); + set_rtc_date_diff(qemu_timedate_diff(&tm)); + qapi_event_send_rtc_change(get_rtc_date_diff()); } static void rtc_set_cmos(RTCState *s, const struct tm *tm) -- Gitee From 3eaa433ca1cbee753698893b7732819ba2e31302 Mon Sep 17 00:00:00 2001 From: Jian Wang Date: Thu, 10 Feb 2022 19:43:55 +0800 Subject: [PATCH 083/486] i386: cache passthrough: Update Intel CPUID4.EAX[25:14] based on vCPU topo On Intel target, when host cache passthrough is disabled we will emulate the guest caches with default values and initialize the shared cpu list of the caches based on vCPU topology. However when host cache passthrough is enabled, the shared cpu list is consistent with host regardless what the vCPU topology is. For example, when cache passthrough is enabled, running a guest with vThreads=1 on a host with pThreads=2, we will get that there are every *two* logical vCPUs sharing a L1/L2 cache, which is not consistent with the vCPU topology (vThreads=1). So let's reinitialize BITs[25:14] of Intel CPUID 4 based on the actual vCPU topology instead of host pCPU topology. Signed-off-by: Jian Wang Signed-off-by: Yanan Wang --- target/i386/cpu.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 868cf3e7e8..c1fe2895fd 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5196,7 +5196,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, { X86CPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - uint32_t die_offset; + uint32_t die_offset, smt_width; uint32_t limit; uint32_t signature[3]; X86CPUTopoInfo topo_info; @@ -5205,6 +5205,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, topo_info.cores_per_die = cs->nr_cores; topo_info.threads_per_core = cs->nr_threads; + die_offset = apicid_die_offset(&topo_info); + smt_width = apicid_smt_width(&topo_info); + /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { limit = env->cpuid_xlevel2; @@ -5272,8 +5275,25 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { host_cpuid(index, count, eax, ebx, ecx, edx); - /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ - *eax &= ~0xFC000000; + /* + * QEMU gives out its own APIC IDs, never pass down bits 31..26. + * Update the cache topo bits 25..14, according to the guest + * vCPU topology instead of the host pCPU topology. + */ + *eax &= ~0xFFFFC000; + switch (count) { + case 0: /* L1 dcache info */ + case 1: /* L1 icache info */ + case 2: /* L2 cache info */ + *eax |= ((1 << smt_width) - 1) << 14; + break; + case 3: /* L3 cache info */ + *eax |= ((1 << die_offset) - 1) << 14; + break; + default: /* end of info */ + *eax = *ebx = *ecx = *edx = 0; + break; + } if ((*eax & 31) && cs->nr_cores > 1) { *eax |= (cs->nr_cores - 1) << 26; } @@ -5298,7 +5318,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - die_offset = apicid_die_offset(&topo_info); if (cpu->enable_l3_cache) { encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, (1 << die_offset), cs->nr_cores, -- Gitee From 475988057789a1f4dcd7354c8a07fd37dcbac79f Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Thu, 10 Feb 2022 20:06:01 +0800 Subject: [PATCH 084/486] i386: cache passthrough: Update AMD 8000_001D.EAX[25:14] based on vCPU topo On AMD target, when host cache passthrough is disabled we will emulate the guest caches with default values and initialize the shared cpu list of the caches based on vCPU topology. However when host cache passthrough is enabled, the shared cpu list is consistent with host regardless what the vCPU topology is. For example, when cache passthrough is enabled, running a guest with vThreads=1 on a host with pThreads=2, we will get that there are every *two* logical vCPUs sharing a L1/L2 cache, which is not consistent with the vCPU topology (vThreads=1). So let's reinitialize BITs[25:14] of AMD CPUID 8000_001D.EAX based on the actual vCPU topology instead of host pCPU topology. Signed-off-by: Yanan Wang --- target/i386/cpu.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c1fe2895fd..002e32650d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5724,9 +5724,31 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; case 0x8000001D: + /* Populate AMD Processor Cache Information */ *eax = 0; if (cpu->cache_info_passthrough) { host_cpuid(index, count, eax, ebx, ecx, edx); + + /* + * Clear BITs[25:14] and then update them based on the guest + * vCPU topology, like what we do in encode_cache_cpuid8000001d + * when cache_info_passthrough is not enabled. + */ + *eax &= ~0x03FFC000; + switch (count) { + case 0: /* L1 dcache info */ + case 1: /* L1 icache info */ + case 2: /* L2 cache info */ + *eax |= ((topo_info.threads_per_core - 1) << 14); + break; + case 3: /* L3 cache info */ + *eax |= ((topo_info.cores_per_die * + topo_info.threads_per_core - 1) << 14); + break; + default: /* end of info */ + *eax = *ebx = *ecx = *edx = 0; + break; + } break; } switch (count) { -- Gitee From 40512773625a4f8ddd96a5af924f119b89a14706 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sat, 8 May 2021 17:31:03 +0800 Subject: [PATCH 085/486] linux-headers: update against 5.10 and manual clear vfio dirty log series The new capability VFIO_DIRTY_LOG_MANUAL_CLEAR and the new ioctl VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP_NOCLEAR and VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP have been introduced in the kernel, update the header to add them. Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang --- linux-headers/linux/vfio.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index e680594f27..f4ff038e8c 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -52,6 +52,16 @@ /* Supports the vaddr flag for DMA map and unmap */ #define VFIO_UPDATE_VADDR 10 +/* + * The vfio_iommu driver may support user clears dirty log manually, which means + * dirty log can be requested to not cleared automatically after dirty log is + * copied to userspace, it's user's duty to clear dirty log. + * + * Note: please refer to VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP_NOCLEAR and + * VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP. + */ +#define VFIO_DIRTY_LOG_MANUAL_CLEAR 11 + /* * The IOCTL interface is designed for extensibility by embedding the * structure length (argsz) and flags into structures passed between @@ -1196,8 +1206,30 @@ struct vfio_iommu_type1_dma_unmap { * actual bitmap. If dirty pages logging is not enabled, an error will be * returned. * - * Only one of the flags _START, _STOP and _GET may be specified at a time. + * The VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP_NOCLEAR flag is almost same as + * VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP, except that it requires underlying + * dirty bitmap is not cleared automatically. The user can clear it manually by + * calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP flag set. * + * Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP flag set, + * instructs the IOMMU driver to clear the dirty status of pages in a bitmap + * for IOMMU container for a given IOVA range. The user must specify the IOVA + * range, the bitmap and the pgsize through the structure + * vfio_iommu_type1_dirty_bitmap_get in the data[] portion. This interface + * supports clearing a bitmap of the smallest supported pgsize only and can be + * modified in future to clear a bitmap of any specified supported pgsize. The + * user must provide a memory area for the bitmap memory and specify its size + * in bitmap.size. One bit is used to represent one page consecutively starting + * from iova offset. The user should provide page size in bitmap.pgsize field. + * A bit set in the bitmap indicates that the page at that offset from iova is + * cleared the dirty status, and dirty tracking is re-enabled for that page. The + * caller must set argsz to a value including the size of structure + * vfio_iommu_dirty_bitmap_get, but excluing the size of the actual bitmap. If + * dirty pages logging is not enabled, an error will be returned. Note: user + * should clear dirty log before handle corresponding dirty pages. + * + * Only one of the flags _START, _STOP, _GET, _GET_NOCLEAR_, and _CLEAR may be + * specified at a time. */ struct vfio_iommu_type1_dirty_bitmap { __u32 argsz; @@ -1205,6 +1237,8 @@ struct vfio_iommu_type1_dirty_bitmap { #define VFIO_IOMMU_DIRTY_PAGES_FLAG_START (1 << 0) #define VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP (1 << 1) #define VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP (1 << 2) +#define VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP_NOCLEAR (1 << 3) +#define VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP (1 << 4) __u8 data[]; }; -- Gitee From ac1bf3edcd2b807cf81ada500716f13b1394d58e Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sat, 8 May 2021 17:31:04 +0800 Subject: [PATCH 086/486] vfio: Maintain DMA mapping range for the container When synchronizing dirty bitmap from kernel VFIO we do it in a per-iova-range fashion and we allocate the userspace bitmap for each of the ioctl. This patch introduces `struct VFIODMARange` to describe a range of the given DMA mapping with respect to a VFIO_IOMMU_MAP_DMA operation, and make the bitmap cache of this range be persistent so that we don't need to g_try_malloc0() every time. Note that the new structure is almost a copy of `struct vfio_iommu_type1_dma_map` but only internally used by QEMU. More importantly, the cached per-iova-range dirty bitmap will be further used when we want to add support for the CLEAR_BITMAP and this cached bitmap will be used to guarantee we don't clear any unknown dirty bits otherwise that can be a severe data loss issue for migration code. It's pretty intuitive to maintain a bitmap per container since we perform log_sync at this granule. But I don't know how to deal with things like memory hot-{un}plug, sparse DMA mappings, etc. Suggestions welcome. * yet something to-do: - can't work with guest viommu - no locks - etc [ The idea and even the commit message are largely inherited from kvm side. See commit 9f4bf4baa8b820c7930e23c9566c9493db7e1d25. ] Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 62 +++++++++++++++++++++++++++++++---- include/hw/vfio/vfio-common.h | 9 +++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 080046e3f5..86ea784919 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -445,6 +445,29 @@ unmap_exit: return ret; } +static VFIODMARange *vfio_lookup_match_range(VFIOContainer *container, + hwaddr start_addr, hwaddr size) +{ + VFIODMARange *qrange; + + QLIST_FOREACH(qrange, &container->dma_list, next) { + if (qrange->iova == start_addr && qrange->size == size) { + return qrange; + } + } + return NULL; +} + +static void vfio_dma_range_init_dirty_bitmap(VFIODMARange *qrange) +{ + uint64_t pages, size; + + pages = REAL_HOST_PAGE_ALIGN(qrange->size) / qemu_real_host_page_size; + size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / BITS_PER_BYTE; + + qrange->bitmap = g_malloc0(size); +} + /* * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 */ @@ -458,12 +481,29 @@ static int vfio_dma_unmap(VFIOContainer *container, .iova = iova, .size = size, }; + VFIODMARange *qrange; if (iotlb && container->dirty_pages_supported && vfio_devices_all_running_and_saving(container)) { return vfio_dma_unmap_bitmap(container, iova, size, iotlb); } + /* + * unregister the DMA range + * + * It seems that the memory layer will give us the same section as the one + * used in region_add(). Otherwise it'll be complicated to manipulate the + * bitmap across region_{add,del}. Is there any guarantee? + * + * But there is really not such a restriction on the kernel interface + * (VFIO_IOMMU_DIRTY_PAGES_FLAG_{UN}MAP_DMA, etc). + */ + qrange = vfio_lookup_match_range(container, iova, size); + assert(qrange); + g_free(qrange->bitmap); + QLIST_REMOVE(qrange, next); + g_free(qrange); + while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { /* * The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c @@ -500,6 +540,14 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova, .iova = iova, .size = size, }; + VFIODMARange *qrange; + + qrange = g_malloc0(sizeof(*qrange)); + qrange->iova = iova; + qrange->size = size; + QLIST_INSERT_HEAD(&container->dma_list, qrange, next); + /* XXX allocate the dirty bitmap on demand */ + vfio_dma_range_init_dirty_bitmap(qrange); if (!readonly) { map.flags |= VFIO_DMA_MAP_FLAG_WRITE; @@ -1256,9 +1304,14 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, { struct vfio_iommu_type1_dirty_bitmap *dbitmap; struct vfio_iommu_type1_dirty_bitmap_get *range; + VFIODMARange *qrange; uint64_t pages; int ret; + qrange = vfio_lookup_match_range(container, iova, size); + /* the same as vfio_dma_unmap() */ + assert(qrange); + dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range)); dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range); @@ -1277,11 +1330,8 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size; range->bitmap.size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / BITS_PER_BYTE; - range->bitmap.data = g_try_malloc0(range->bitmap.size); - if (!range->bitmap.data) { - ret = -ENOMEM; - goto err_out; - } + + range->bitmap.data = (__u64 *)qrange->bitmap; ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap); if (ret) { @@ -1297,7 +1347,6 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, trace_vfio_get_dirty_bitmap(container->fd, range->iova, range->size, range->bitmap.size, ram_addr); err_out: - g_free(range->bitmap.data); g_free(dbitmap); return ret; @@ -2061,6 +2110,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, QLIST_INIT(&container->giommu_list); QLIST_INIT(&container->hostwin_list); QLIST_INIT(&container->vrdl_list); + QLIST_INIT(&container->dma_list); ret = vfio_init_container(container, group->fd, errp); if (ret) { diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 8af11b0a76..20b9c8a1d3 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -76,6 +76,14 @@ typedef struct VFIOAddressSpace { struct VFIOGroup; +typedef struct VFIODMARange { + QLIST_ENTRY(VFIODMARange) next; + hwaddr iova; + size_t size; + void *vaddr; /* unused */ + unsigned long *bitmap; /* dirty bitmap cache for this range */ +} VFIODMARange; + typedef struct VFIOContainer { VFIOAddressSpace *space; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ @@ -93,6 +101,7 @@ typedef struct VFIOContainer { QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; QLIST_HEAD(, VFIOGroup) group_list; QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; + QLIST_HEAD(, VFIODMARange) dma_list; QLIST_ENTRY(VFIOContainer) next; } VFIOContainer; -- Gitee From 815258f81a660ad87272191dca4a9726cb2bf5b2 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sat, 8 May 2021 17:31:05 +0800 Subject: [PATCH 087/486] vfio/migration: Add support for manual clear vfio dirty log The new capability VFIO_DIRTY_LOG_MANUAL_CLEAR and the new ioctl VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP_NOCLEAR and VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP have been introduced in the kernel, tweak the userspace side to use them. Check if the kernel supports VFIO_DIRTY_LOG_MANUAL_CLEAR and provide the log_clear() hook for vfio_memory_listener. If the kernel supports it, deliever the clear message to kernel. Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 149 +++++++++++++++++++++++++++++++++- include/hw/vfio/vfio-common.h | 1 + 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 86ea784919..6cb91e7ffd 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1315,7 +1315,9 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range)); dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range); - dbitmap->flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP; + dbitmap->flags = container->dirty_log_manual_clear ? + VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP_NOCLEAR : + VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP; range = (struct vfio_iommu_type1_dirty_bitmap_get *)&dbitmap->data; range->iova = iova; range->size = size; @@ -1491,6 +1493,141 @@ static void vfio_listener_log_sync(MemoryListener *listener, } } +/* + * I'm not sure if there's any alignment requirement for the CLEAR_BITMAP + * ioctl. But copy from kvm side and align {start, size} with 64 pages. + * + * I think the code can be simplified a lot if no alignment requirement. + */ +#define VFIO_CLEAR_LOG_SHIFT 6 +#define VFIO_CLEAR_LOG_ALIGN (qemu_real_host_page_size << VFIO_CLEAR_LOG_SHIFT) +#define VFIO_CLEAR_LOG_MASK (-VFIO_CLEAR_LOG_ALIGN) + +static int vfio_log_clear_one_range(VFIOContainer *container, + VFIODMARange *qrange, uint64_t start, uint64_t size) +{ + struct vfio_iommu_type1_dirty_bitmap *dbitmap; + struct vfio_iommu_type1_dirty_bitmap_get *range; + + dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range)); + + dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range); + dbitmap->flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_CLEAR_BITMAP; + range = (struct vfio_iommu_type1_dirty_bitmap_get *)&dbitmap->data; + + /* + * Now let's deal with the actual bitmap, which is almost the same + * as the kvm side. + */ + uint64_t end, bmap_start, start_delta, bmap_npages; + unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size; + int ret; + + bmap_start = start & VFIO_CLEAR_LOG_MASK; + start_delta = start - bmap_start; + bmap_start /= psize; + + bmap_npages = DIV_ROUND_UP(size + start_delta, VFIO_CLEAR_LOG_ALIGN) + << VFIO_CLEAR_LOG_SHIFT; + end = qrange->size / psize; + if (bmap_npages > end - bmap_start) { + bmap_npages = end - bmap_start; + } + start_delta /= psize; + + if (start_delta) { + bmap_clear = bitmap_new(bmap_npages); + bitmap_copy_with_src_offset(bmap_clear, qrange->bitmap, + bmap_start, start_delta + size / psize); + bitmap_clear(bmap_clear, 0, start_delta); + range->bitmap.data = (__u64 *)bmap_clear; + } else { + range->bitmap.data = (__u64 *)(qrange->bitmap + BIT_WORD(bmap_start)); + } + + range->iova = qrange->iova + bmap_start * psize; + range->size = bmap_npages * psize; + range->bitmap.size = ROUND_UP(bmap_npages, sizeof(__u64) * BITS_PER_BYTE) / + BITS_PER_BYTE; + range->bitmap.pgsize = qemu_real_host_page_size; + + ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap); + if (ret) { + error_report("Failed to clear dirty log for iova: 0x%"PRIx64 + " size: 0x%"PRIx64" err: %d", (uint64_t)range->iova, + (uint64_t)range->size, errno); + goto err_out; + } + + bitmap_clear(qrange->bitmap, bmap_start + start_delta, size / psize); +err_out: + g_free(bmap_clear); + g_free(dbitmap); + return 0; +} + +static int vfio_physical_log_clear(VFIOContainer *container, + MemoryRegionSection *section) +{ + uint64_t start, size, offset, count; + VFIODMARange *qrange; + int ret = 0; + + if (!container->dirty_log_manual_clear) { + /* No need to do explicit clear */ + return ret; + } + + start = section->offset_within_address_space; + size = int128_get64(section->size); + + if (!size) { + return ret; + } + + QLIST_FOREACH(qrange, &container->dma_list, next) { + /* + * Discard ranges that do not overlap the section (e.g., the + * Memory BAR regions of the device) + */ + if (qrange->iova > start + size - 1 || + start > qrange->iova + qrange->size - 1) { + continue; + } + + if (start >= qrange->iova) { + /* The range starts before section or is aligned to it. */ + offset = start - qrange->iova; + count = MIN(qrange->size - offset, size); + } else { + /* The range starts after section. */ + offset = 0; + count = MIN(qrange->size, size - (qrange->iova - start)); + } + ret = vfio_log_clear_one_range(container, qrange, offset, count); + if (ret < 0) { + break; + } + } + + return ret; +} + +static void vfio_listener_log_clear(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, listener); + + if (vfio_listener_skipped_section(section) || + !container->dirty_pages_supported) { + return; + } + + if (vfio_devices_all_dirty_tracking(container)) { + vfio_physical_log_clear(container, section); + } +} + static const MemoryListener vfio_memory_listener = { .name = "vfio", .region_add = vfio_listener_region_add, @@ -1498,6 +1635,7 @@ static const MemoryListener vfio_memory_listener = { .log_global_start = vfio_listener_log_global_start, .log_global_stop = vfio_listener_log_global_stop, .log_sync = vfio_listener_log_sync, + .log_clear = vfio_listener_log_clear, }; static void vfio_listener_release(VFIOContainer *container) @@ -1925,7 +2063,7 @@ static int vfio_get_iommu_type(VFIOContainer *container, static int vfio_init_container(VFIOContainer *container, int group_fd, Error **errp) { - int iommu_type, ret; + int iommu_type, dirty_log_manual_clear, ret; iommu_type = vfio_get_iommu_type(container, errp); if (iommu_type < 0) { @@ -1954,6 +2092,13 @@ static int vfio_init_container(VFIOContainer *container, int group_fd, } container->iommu_type = iommu_type; + + dirty_log_manual_clear = ioctl(container->fd, VFIO_CHECK_EXTENSION, + VFIO_DIRTY_LOG_MANUAL_CLEAR); + if (dirty_log_manual_clear) { + container->dirty_log_manual_clear = dirty_log_manual_clear; + } + return 0; } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 20b9c8a1d3..0234f5e1b1 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -93,6 +93,7 @@ typedef struct VFIOContainer { Error *error; bool initialized; bool dirty_pages_supported; + bool dirty_log_manual_clear; uint64_t dirty_pgsizes; uint64_t max_dirty_bitmap_size; unsigned long pgsizes; -- Gitee From 694acf3c321908d26ce508842b7bd076664ffbc6 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 9 May 2019 10:23:42 -0400 Subject: [PATCH 088/486] update-linux-headers: Import iommu.h Update the script to import the new iommu.h uapi header. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- scripts/update-linux-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index fea4d6eb65..acde610733 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -144,7 +144,7 @@ done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ +for header in kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h iommu.h \ psci.h psp-sev.h userfaultfd.h mman.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done -- Gitee From 36b65d7312a343cb636e6963b8262dce9420ebc6 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Fri, 30 Jul 2021 09:15:31 +0800 Subject: [PATCH 089/486] vfio.h and iommu.h header update against 5.10 Signed-off-by: Kunkun Jiang --- linux-headers/linux/iommu.h | 395 ++++++++++++++++++++++++++++++++++++ linux-headers/linux/vfio.h | 220 +++++++++++++++++++- 2 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 linux-headers/linux/iommu.h diff --git a/linux-headers/linux/iommu.h b/linux-headers/linux/iommu.h new file mode 100644 index 0000000000..773b7dc2d6 --- /dev/null +++ b/linux-headers/linux/iommu.h @@ -0,0 +1,395 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * IOMMU user API definitions + */ + +#ifndef IOMMU_H +#define IOMMU_H + +#include + +#define IOMMU_FAULT_PERM_READ (1 << 0) /* read */ +#define IOMMU_FAULT_PERM_WRITE (1 << 1) /* write */ +#define IOMMU_FAULT_PERM_EXEC (1 << 2) /* exec */ +#define IOMMU_FAULT_PERM_PRIV (1 << 3) /* privileged */ + +/* Generic fault types, can be expanded IRQ remapping fault */ +enum iommu_fault_type { + IOMMU_FAULT_DMA_UNRECOV = 1, /* unrecoverable fault */ + IOMMU_FAULT_PAGE_REQ, /* page request fault */ +}; + +enum iommu_fault_reason { + IOMMU_FAULT_REASON_UNKNOWN = 0, + + /* Could not access the PASID table (fetch caused external abort) */ + IOMMU_FAULT_REASON_PASID_FETCH, + + /* PASID entry is invalid or has configuration errors */ + IOMMU_FAULT_REASON_BAD_PASID_ENTRY, + + /* + * PASID is out of range (e.g. exceeds the maximum PASID + * supported by the IOMMU) or disabled. + */ + IOMMU_FAULT_REASON_PASID_INVALID, + + /* + * An external abort occurred fetching (or updating) a translation + * table descriptor + */ + IOMMU_FAULT_REASON_WALK_EABT, + + /* + * Could not access the page table entry (Bad address), + * actual translation fault + */ + IOMMU_FAULT_REASON_PTE_FETCH, + + /* Protection flag check failed */ + IOMMU_FAULT_REASON_PERMISSION, + + /* access flag check failed */ + IOMMU_FAULT_REASON_ACCESS, + + /* Output address of a translation stage caused Address Size fault */ + IOMMU_FAULT_REASON_OOR_ADDRESS, +}; + +/** + * struct iommu_fault_unrecoverable - Unrecoverable fault data + * @reason: reason of the fault, from &enum iommu_fault_reason + * @flags: parameters of this fault (IOMMU_FAULT_UNRECOV_* values) + * @pasid: Process Address Space ID + * @perm: requested permission access using by the incoming transaction + * (IOMMU_FAULT_PERM_* values) + * @addr: offending page address + * @fetch_addr: address that caused a fetch abort, if any + */ +struct iommu_fault_unrecoverable { + __u32 reason; +#define IOMMU_FAULT_UNRECOV_PASID_VALID (1 << 0) +#define IOMMU_FAULT_UNRECOV_ADDR_VALID (1 << 1) +#define IOMMU_FAULT_UNRECOV_FETCH_ADDR_VALID (1 << 2) + __u32 flags; + __u32 pasid; + __u32 perm; + __u64 addr; + __u64 fetch_addr; +}; + +/** + * struct iommu_fault_page_request - Page Request data + * @flags: encodes whether the corresponding fields are valid and whether this + * is the last page in group (IOMMU_FAULT_PAGE_REQUEST_* values). + * When IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID is set, the page response + * must have the same PASID value as the page request. When it is clear, + * the page response should not have a PASID. + * @pasid: Process Address Space ID + * @grpid: Page Request Group Index + * @perm: requested page permissions (IOMMU_FAULT_PERM_* values) + * @addr: page address + * @private_data: device-specific private information + */ +struct iommu_fault_page_request { +#define IOMMU_FAULT_PAGE_REQUEST_PASID_VALID (1 << 0) +#define IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE (1 << 1) +#define IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA (1 << 2) +#define IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID (1 << 3) + __u32 flags; + __u32 pasid; + __u32 grpid; + __u32 perm; + __u64 addr; + __u64 private_data[2]; +}; + +/** + * struct iommu_fault - Generic fault data + * @type: fault type from &enum iommu_fault_type + * @padding: reserved for future use (should be zero) + * @event: fault event, when @type is %IOMMU_FAULT_DMA_UNRECOV + * @prm: Page Request message, when @type is %IOMMU_FAULT_PAGE_REQ + * @padding2: sets the fault size to allow for future extensions + */ +struct iommu_fault { + __u32 type; + __u32 padding; + union { + struct iommu_fault_unrecoverable event; + struct iommu_fault_page_request prm; + __u8 padding2[56]; + }; +}; + +/** + * enum iommu_page_response_code - Return status of fault handlers + * @IOMMU_PAGE_RESP_SUCCESS: Fault has been handled and the page tables + * populated, retry the access. This is "Success" in PCI PRI. + * @IOMMU_PAGE_RESP_FAILURE: General error. Drop all subsequent faults from + * this device if possible. This is "Response Failure" in PCI PRI. + * @IOMMU_PAGE_RESP_INVALID: Could not handle this fault, don't retry the + * access. This is "Invalid Request" in PCI PRI. + */ +enum iommu_page_response_code { + IOMMU_PAGE_RESP_SUCCESS = 0, + IOMMU_PAGE_RESP_INVALID, + IOMMU_PAGE_RESP_FAILURE, +}; + +/** + * struct iommu_page_response - Generic page response information + * @argsz: User filled size of this data + * @version: API version of this structure + * @flags: encodes whether the corresponding fields are valid + * (IOMMU_FAULT_PAGE_RESPONSE_* values) + * @pasid: Process Address Space ID + * @grpid: Page Request Group Index + * @code: response code from &enum iommu_page_response_code + */ +struct iommu_page_response { + __u32 argsz; +#define IOMMU_PAGE_RESP_VERSION_1 1 + __u32 version; +#define IOMMU_PAGE_RESP_PASID_VALID (1 << 0) + __u32 flags; + __u32 pasid; + __u32 grpid; + __u32 code; +}; + +/* defines the granularity of the invalidation */ +enum iommu_inv_granularity { + IOMMU_INV_GRANU_DOMAIN, /* domain-selective invalidation */ + IOMMU_INV_GRANU_PASID, /* PASID-selective invalidation */ + IOMMU_INV_GRANU_ADDR, /* page-selective invalidation */ + IOMMU_INV_GRANU_NR, /* number of invalidation granularities */ +}; + +/** + * struct iommu_inv_addr_info - Address Selective Invalidation Structure + * + * @flags: indicates the granularity of the address-selective invalidation + * - If the PASID bit is set, the @pasid field is populated and the invalidation + * relates to cache entries tagged with this PASID and matching the address + * range. + * - If ARCHID bit is set, @archid is populated and the invalidation relates + * to cache entries tagged with this architecture specific ID and matching + * the address range. + * - Both PASID and ARCHID can be set as they may tag different caches. + * - If neither PASID or ARCHID is set, global addr invalidation applies. + * - The LEAF flag indicates whether only the leaf PTE caching needs to be + * invalidated and other paging structure caches can be preserved. + * @pasid: process address space ID + * @archid: architecture-specific ID + * @addr: first stage/level input address + * @granule_size: page/block size of the mapping in bytes + * @nb_granules: number of contiguous granules to be invalidated + */ +struct iommu_inv_addr_info { +#define IOMMU_INV_ADDR_FLAGS_PASID (1 << 0) +#define IOMMU_INV_ADDR_FLAGS_ARCHID (1 << 1) +#define IOMMU_INV_ADDR_FLAGS_LEAF (1 << 2) + __u32 flags; + __u32 archid; + __u64 pasid; + __u64 addr; + __u64 granule_size; + __u64 nb_granules; +}; + +/** + * struct iommu_inv_pasid_info - PASID Selective Invalidation Structure + * + * @flags: indicates the granularity of the PASID-selective invalidation + * - If the PASID bit is set, the @pasid field is populated and the invalidation + * relates to cache entries tagged with this PASID and matching the address + * range. + * - If the ARCHID bit is set, the @archid is populated and the invalidation + * relates to cache entries tagged with this architecture specific ID and + * matching the address range. + * - Both PASID and ARCHID can be set as they may tag different caches. + * - At least one of PASID or ARCHID must be set. + * @pasid: process address space ID + * @archid: architecture-specific ID + */ +struct iommu_inv_pasid_info { +#define IOMMU_INV_PASID_FLAGS_PASID (1 << 0) +#define IOMMU_INV_PASID_FLAGS_ARCHID (1 << 1) + __u32 flags; + __u32 archid; + __u64 pasid; +}; + +/** + * struct iommu_cache_invalidate_info - First level/stage invalidation + * information + * @argsz: User filled size of this data + * @version: API version of this structure + * @cache: bitfield that allows to select which caches to invalidate + * @granularity: defines the lowest granularity used for the invalidation: + * domain > PASID > addr + * @padding: reserved for future use (should be zero) + * @pasid_info: invalidation data when @granularity is %IOMMU_INV_GRANU_PASID + * @addr_info: invalidation data when @granularity is %IOMMU_INV_GRANU_ADDR + * + * Not all the combinations of cache/granularity are valid: + * + * +--------------+---------------+---------------+---------------+ + * | type / | DEV_IOTLB | IOTLB | PASID | + * | granularity | | | cache | + * +==============+===============+===============+===============+ + * | DOMAIN | N/A | Y | Y | + * +--------------+---------------+---------------+---------------+ + * | PASID | Y | Y | Y | + * +--------------+---------------+---------------+---------------+ + * | ADDR | Y | Y | N/A | + * +--------------+---------------+---------------+---------------+ + * + * Invalidations by %IOMMU_INV_GRANU_DOMAIN don't take any argument other than + * @version and @cache. + * + * If multiple cache types are invalidated simultaneously, they all + * must support the used granularity. + */ +struct iommu_cache_invalidate_info { + __u32 argsz; +#define IOMMU_CACHE_INVALIDATE_INFO_VERSION_1 1 + __u32 version; +/* IOMMU paging structure cache */ +#define IOMMU_CACHE_INV_TYPE_IOTLB (1 << 0) /* IOMMU IOTLB */ +#define IOMMU_CACHE_INV_TYPE_DEV_IOTLB (1 << 1) /* Device IOTLB */ +#define IOMMU_CACHE_INV_TYPE_PASID (1 << 2) /* PASID cache */ +#define IOMMU_CACHE_INV_TYPE_NR (3) + __u8 cache; + __u8 granularity; + __u8 padding[6]; + union { + struct iommu_inv_pasid_info pasid_info; + struct iommu_inv_addr_info addr_info; + } granu; +}; + +/** + * struct iommu_gpasid_bind_data_vtd - Intel VT-d specific data on device and guest + * SVA binding. + * + * @flags: VT-d PASID table entry attributes + * @pat: Page attribute table data to compute effective memory type + * @emt: Extended memory type + * + * Only guest vIOMMU selectable and effective options are passed down to + * the host IOMMU. + */ +struct iommu_gpasid_bind_data_vtd { +#define IOMMU_SVA_VTD_GPASID_SRE (1 << 0) /* supervisor request */ +#define IOMMU_SVA_VTD_GPASID_EAFE (1 << 1) /* extended access enable */ +#define IOMMU_SVA_VTD_GPASID_PCD (1 << 2) /* page-level cache disable */ +#define IOMMU_SVA_VTD_GPASID_PWT (1 << 3) /* page-level write through */ +#define IOMMU_SVA_VTD_GPASID_EMTE (1 << 4) /* extended mem type enable */ +#define IOMMU_SVA_VTD_GPASID_CD (1 << 5) /* PASID-level cache disable */ +#define IOMMU_SVA_VTD_GPASID_LAST (1 << 6) + __u64 flags; + __u32 pat; + __u32 emt; +}; + +#define IOMMU_SVA_VTD_GPASID_MTS_MASK (IOMMU_SVA_VTD_GPASID_CD | \ + IOMMU_SVA_VTD_GPASID_EMTE | \ + IOMMU_SVA_VTD_GPASID_PCD | \ + IOMMU_SVA_VTD_GPASID_PWT) + +/** + * struct iommu_gpasid_bind_data - Information about device and guest PASID binding + * @argsz: User filled size of this data + * @version: Version of this data structure + * @format: PASID table entry format + * @flags: Additional information on guest bind request + * @gpgd: Guest page directory base of the guest mm to bind + * @hpasid: Process address space ID used for the guest mm in host IOMMU + * @gpasid: Process address space ID used for the guest mm in guest IOMMU + * @addr_width: Guest virtual address width + * @padding: Reserved for future use (should be zero) + * @vtd: Intel VT-d specific data + * + * Guest to host PASID mapping can be an identity or non-identity, where guest + * has its own PASID space. For non-identify mapping, guest to host PASID lookup + * is needed when VM programs guest PASID into an assigned device. VMM may + * trap such PASID programming then request host IOMMU driver to convert guest + * PASID to host PASID based on this bind data. + */ +struct iommu_gpasid_bind_data { + __u32 argsz; +#define IOMMU_GPASID_BIND_VERSION_1 1 + __u32 version; +#define IOMMU_PASID_FORMAT_INTEL_VTD 1 +#define IOMMU_PASID_FORMAT_LAST 2 + __u32 format; + __u32 addr_width; +#define IOMMU_SVA_GPASID_VAL (1 << 0) /* guest PASID valid */ + __u64 flags; + __u64 gpgd; + __u64 hpasid; + __u64 gpasid; + __u8 padding[8]; + /* Vendor specific data */ + union { + struct iommu_gpasid_bind_data_vtd vtd; + } vendor; +}; + +/** + * struct iommu_pasid_smmuv3 - ARM SMMUv3 Stream Table Entry stage 1 related + * information + * @version: API version of this structure + * @s1fmt: STE s1fmt (format of the CD table: single CD, linear table + * or 2-level table) + * @s1dss: STE s1dss (specifies the behavior when @pasid_bits != 0 + * and no PASID is passed along with the incoming transaction) + * @padding: reserved for future use (should be zero) + * + * The PASID table is referred to as the Context Descriptor (CD) table on ARM + * SMMUv3. Please refer to the ARM SMMU 3.x spec (ARM IHI 0070A) for full + * details. + */ +struct iommu_pasid_smmuv3 { +#define PASID_TABLE_SMMUV3_CFG_VERSION_1 1 + __u32 version; + __u8 s1fmt; + __u8 s1dss; + __u8 padding[2]; +}; + +/** + * struct iommu_pasid_table_config - PASID table data used to bind guest PASID + * table to the host IOMMU + * @argsz: User filled size of this data + * @version: API version to prepare for future extensions + * @base_ptr: guest physical address of the PASID table + * @format: format of the PASID table + * @pasid_bits: number of PASID bits used in the PASID table + * @config: indicates whether the guest translation stage must + * be translated, bypassed or aborted. + * @padding: reserved for future use (should be zero) + * @vendor_data.smmuv3: table information when @format is + * %IOMMU_PASID_FORMAT_SMMUV3 + */ +struct iommu_pasid_table_config { + __u32 argsz; +#define PASID_TABLE_CFG_VERSION_1 1 + __u32 version; + __u64 base_ptr; +#define IOMMU_PASID_FORMAT_SMMUV3 1 + __u32 format; + __u8 pasid_bits; +#define IOMMU_PASID_CONFIG_TRANSLATE 1 +#define IOMMU_PASID_CONFIG_BYPASS 2 +#define IOMMU_PASID_CONFIG_ABORT 3 + __u8 config; + __u8 padding[2]; + union { + struct iommu_pasid_smmuv3 smmuv3; + } vendor_data; +}; + +#endif /* _UAPI_IOMMU_H */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index f4ff038e8c..cf8e208fac 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -14,6 +14,7 @@ #include #include +#include #define VFIO_API_VERSION 0 @@ -334,6 +335,7 @@ struct vfio_region_info_cap_type { #define VFIO_REGION_TYPE_GFX (1) #define VFIO_REGION_TYPE_CCW (2) #define VFIO_REGION_TYPE_MIGRATION (3) +#define VFIO_REGION_TYPE_NESTED (4) /* sub-types for VFIO_REGION_TYPE_PCI_* */ @@ -362,6 +364,10 @@ struct vfio_region_info_cap_type { /* sub-types for VFIO_REGION_TYPE_GFX */ #define VFIO_REGION_SUBTYPE_GFX_EDID (1) +/* sub-types for VFIO_REGION_TYPE_NESTED */ +#define VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT (1) +#define VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT_RESPONSE (2) + /** * struct vfio_region_gfx_edid - EDID region layout. * @@ -721,11 +727,30 @@ struct vfio_irq_info { #define VFIO_IRQ_INFO_MASKABLE (1 << 1) #define VFIO_IRQ_INFO_AUTOMASKED (1 << 2) #define VFIO_IRQ_INFO_NORESIZE (1 << 3) +#define VFIO_IRQ_INFO_FLAG_CAPS (1 << 4) /* Info supports caps */ __u32 index; /* IRQ index */ __u32 count; /* Number of IRQs within this index */ + __u32 cap_offset; /* Offset within info struct of first cap */ }; #define VFIO_DEVICE_GET_IRQ_INFO _IO(VFIO_TYPE, VFIO_BASE + 9) +/* + * The irq type capability allows IRQs unique to a specific device or + * class of devices to be exposed. + * + * The structures below define version 1 of this capability. + */ +#define VFIO_IRQ_INFO_CAP_TYPE 3 + +struct vfio_irq_info_cap_type { + struct vfio_info_cap_header header; + __u32 type; /* global per bus driver */ + __u32 subtype; /* type specific */ +}; + +#define VFIO_IRQ_TYPE_NESTED (1) +#define VFIO_IRQ_SUBTYPE_DMA_FAULT (1) + /** * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set) * @@ -827,7 +852,8 @@ enum { VFIO_PCI_MSIX_IRQ_INDEX, VFIO_PCI_ERR_IRQ_INDEX, VFIO_PCI_REQ_IRQ_INDEX, - VFIO_PCI_NUM_IRQS + VFIO_PCI_NUM_IRQS = 5 /* Fixed user ABI, IRQ indexes >=5 use */ + /* device specific cap to define content */ }; /* @@ -1012,6 +1038,68 @@ struct vfio_device_feature { */ #define VFIO_DEVICE_FEATURE_PCI_VF_TOKEN (0) +/* + * Capability exposed by the DMA fault region + * @version: ABI version + */ +#define VFIO_REGION_INFO_CAP_DMA_FAULT 6 + +struct vfio_region_info_cap_fault { + struct vfio_info_cap_header header; + __u32 version; +}; + +/* + * Capability exposed by the DMA fault response region + * @version: ABI version + */ +#define VFIO_REGION_INFO_CAP_DMA_FAULT_RESPONSE 7 + +struct vfio_region_info_cap_fault_response { + struct vfio_info_cap_header header; + __u32 version; +}; + +/* + * DMA Fault Region Layout + * @tail: index relative to the start of the ring buffer at which the + * consumer finds the next item in the buffer + * @entry_size: fault ring buffer entry size in bytes + * @nb_entries: max capacity of the fault ring buffer + * @offset: ring buffer offset relative to the start of the region + * @head: index relative to the start of the ring buffer at which the + * producer (kernel) inserts items into the buffers + */ +struct vfio_region_dma_fault { + /* Write-Only */ + __u32 tail; + /* Read-Only */ + __u32 entry_size; + __u32 nb_entries; + __u32 offset; + __u32 head; +}; + +/* + * DMA Fault Response Region Layout + * @head: index relative to the start of the ring buffer at which the + * producer (userspace) insert responses into the buffer + * @entry_size: fault ring buffer entry size in bytes + * @nb_entries: max capacity of the fault ring buffer + * @offset: ring buffer offset relative to the start of the region + * @tail: index relative to the start of the ring buffer at which the + * consumer (kernel) finds the next item in the buffer + */ +struct vfio_region_dma_fault_response { + /* Write-Only */ + __u32 head; + /* Read-Only */ + __u32 entry_size; + __u32 nb_entries; + __u32 offset; + __u32 tail; +}; + /* -------- API for Type1 VFIO IOMMU -------- */ /** @@ -1124,7 +1212,7 @@ struct vfio_iommu_type1_dma_map { struct vfio_bitmap { __u64 pgsize; /* page size for bitmap in bytes */ __u64 size; /* in bytes */ - __u64 *data; /* one bit per page */ + __u64 *data; /* one bit per page */ }; /** @@ -1250,6 +1338,134 @@ struct vfio_iommu_type1_dirty_bitmap_get { #define VFIO_IOMMU_DIRTY_PAGES _IO(VFIO_TYPE, VFIO_BASE + 17) +/* + * VFIO_IOMMU_BIND_PROCESS + * + * Allocate a PASID for a process address space, and use it to attach this + * process to all devices in the container. Devices can then tag their DMA + * traffic with the returned @pasid to perform transactions on the associated + * virtual address space. Mapping and unmapping buffers is performed by standard + * functions such as mmap and malloc. + * + * If flag is VFIO_IOMMU_BIND_PID, @pid contains the pid of a foreign process to + * bind. Otherwise the current task is bound. Given that the caller owns the + * device, setting this flag grants the caller read and write permissions on the + * entire address space of foreign process described by @pid. Therefore, + * permission to perform the bind operation on a foreign process is governed by + * the ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check. See man ptrace(2) + * for more information. + * + * On success, VFIO writes a Process Address Space ID (PASID) into @pasid. This + * ID is unique to a process and can be used on all devices in the container. + * + * On fork, the child inherits the device fd and can use the bonds setup by its + * parent. Consequently, the child has R/W access on the address spaces bound by + * its parent. After an execv, the device fd is closed and the child doesn't + * have access to the address space anymore. + * + * To remove a bond between process and container, VFIO_IOMMU_UNBIND ioctl is + * issued with the same parameters. If a pid was specified in VFIO_IOMMU_BIND, + * it should also be present for VFIO_IOMMU_UNBIND. Otherwise unbind the current + * task from the container. + */ +struct vfio_iommu_type1_bind_process { + __u32 flags; +#define VFIO_IOMMU_BIND_PID (1 << 0) + __u32 pasid; + __s32 pid; +}; + +/* + * Only mode supported at the moment is VFIO_IOMMU_BIND_PROCESS, which takes + * vfio_iommu_type1_bind_process in data. + */ +struct vfio_iommu_type1_bind { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_BIND_PROCESS (1 << 0) + __u8 data[]; +}; + +/* + * VFIO_IOMMU_BIND - _IOWR(VFIO_TYPE, VFIO_BASE + 22, struct vfio_iommu_bind) + * + * Manage address spaces of devices in this container. Initially a TYPE1 + * container can only have one address space, managed with + * VFIO_IOMMU_MAP/UNMAP_DMA. + * + * An IOMMU of type VFIO_TYPE1_NESTING_IOMMU can be managed by both MAP/UNMAP + * and BIND ioctls at the same time. MAP/UNMAP acts on the stage-2 (host) page + * tables, and BIND manages the stage-1 (guest) page tables. Other types of + * IOMMU may allow MAP/UNMAP and BIND to coexist, where MAP/UNMAP controls + * non-PASID traffic and BIND controls PASID traffic. But this depends on the + * underlying IOMMU architecture and isn't guaranteed. + * + * Availability of this feature depends on the device, its bus, the underlying + * IOMMU and the CPU architecture. + * + * returns: 0 on success, -errno on failure. + */ +#define VFIO_IOMMU_BIND _IO(VFIO_TYPE, VFIO_BASE + 22) + +/* + * VFIO_IOMMU_UNBIND - _IOWR(VFIO_TYPE, VFIO_BASE + 23, struct vfio_iommu_bind) + * + * Undo what was done by the corresponding VFIO_IOMMU_BIND ioctl. + */ +#define VFIO_IOMMU_UNBIND _IO(VFIO_TYPE, VFIO_BASE + 23) + +/* + * VFIO_IOMMU_SET_PASID_TABLE - _IOWR(VFIO_TYPE, VFIO_BASE + 18, + * struct vfio_iommu_type1_set_pasid_table) + * + * The SET operation passes a PASID table to the host while the + * UNSET operation detaches the one currently programmed. It is + * allowed to "SET" the table several times without unsetting as + * long as the table config does not stay IOMMU_PASID_CONFIG_TRANSLATE. + */ +struct vfio_iommu_type1_set_pasid_table { + __u32 argsz; + __u32 flags; +#define VFIO_PASID_TABLE_FLAG_SET (1 << 0) +#define VFIO_PASID_TABLE_FLAG_UNSET (1 << 1) + struct iommu_pasid_table_config config; /* used on SET */ +}; + +#define VFIO_IOMMU_SET_PASID_TABLE _IO(VFIO_TYPE, VFIO_BASE + 18) + +/** + * VFIO_IOMMU_CACHE_INVALIDATE - _IOWR(VFIO_TYPE, VFIO_BASE + 19, + * struct vfio_iommu_type1_cache_invalidate) + * + * Propagate guest IOMMU cache invalidation to the host. + */ +struct vfio_iommu_type1_cache_invalidate { + __u32 argsz; + __u32 flags; + struct iommu_cache_invalidate_info info; +}; +#define VFIO_IOMMU_CACHE_INVALIDATE _IO(VFIO_TYPE, VFIO_BASE + 19) + +/** + * VFIO_IOMMU_SET_MSI_BINDING - _IOWR(VFIO_TYPE, VFIO_BASE + 20, + * struct vfio_iommu_type1_set_msi_binding) + * + * Pass a stage 1 MSI doorbell mapping to the host so that this + * latter can build a nested stage2 mapping. Or conversely tear + * down a previously bound stage 1 MSI binding. + */ +struct vfio_iommu_type1_set_msi_binding { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_BIND_MSI (1 << 0) +#define VFIO_IOMMU_UNBIND_MSI (1 << 1) + __u64 iova; /* MSI guest IOVA */ + /* Fields below are used on BIND */ + __u64 gpa; /* MSI guest physical address */ + __u64 size; /* size of stage1 mapping (bytes) */ +}; +#define VFIO_IOMMU_SET_MSI_BINDING _IO(VFIO_TYPE, VFIO_BASE + 20) + /* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */ /* -- Gitee From da97cef20d4ee5a8f3942953836b35e7f7dd974f Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 4 Sep 2018 08:43:05 -0400 Subject: [PATCH 090/486] memory: Add new fields in IOTLBEntry The current IOTLBEntry becomes too simple to interact with some physical IOMMUs. IOTLBs can be invalidated with different granularities: domain, pasid, addr. Current IOTLB entry only offers page selective invalidation. Let's add a granularity field that conveys this information. TLB entries are usually tagged with some ids such as the asid or pasid. When propagating an invalidation command from the guest to the host, we need to pass those IDs. Also we add a leaf field which indicates, in case of invalidation notification, whether only cache entries for the last level of translation are required to be invalidated. A flag field is introduced to inform whether those fields are set. To enforce all existing users do not use those new fields, initialize the IOMMUTLBEvents when needed. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmu-common.c | 2 +- hw/arm/smmuv3.c | 2 +- hw/i386/intel_iommu.c | 6 +++--- hw/ppc/spapr_iommu.c | 2 +- hw/virtio/virtio-iommu.c | 4 ++-- include/exec/memory.h | 36 +++++++++++++++++++++++++++++++++++- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 0459850a93..3a1ecf81d6 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -470,7 +470,7 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) /* Unmap the whole notifier's range */ static void smmu_unmap_notifier_range(IOMMUNotifier *n) { - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; event.type = IOMMU_NOTIFIER_UNMAP; event.entry.target_as = &address_space_memory; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 01b60bee49..94e2c658f8 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -802,7 +802,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, uint8_t tg, uint64_t num_pages) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; uint8_t granule; if (!tg) { diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index f584449d8d..fae282ef5e 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1193,7 +1193,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, uint32_t offset; uint64_t slpte; uint64_t subpage_size, subpage_mask; - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; uint64_t iova = start; uint64_t iova_next; int ret = 0; @@ -2425,7 +2425,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { VTDAddressSpace *vtd_dev_as; - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; struct VTDBus *vtd_bus; hwaddr addr; uint64_t sz; @@ -3481,7 +3481,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) size = remain = end - start + 1; while (remain >= VTD_PAGE_SIZE) { - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; uint64_t mask = dma_aligned_pow2_mask(start, end, s->aw_bits); uint64_t size = mask + 1; diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index db01071858..454df25d44 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -449,7 +449,7 @@ static void spapr_tce_reset(DeviceState *dev) static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba, target_ulong tce) { - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 1b23e8e18c..83ed2b82e6 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -129,7 +129,7 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end, hwaddr paddr, uint32_t flags) { - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; IOMMUAccessFlags perm = IOMMU_ACCESS_FLAG(flags & VIRTIO_IOMMU_MAP_F_READ, flags & VIRTIO_IOMMU_MAP_F_WRITE); @@ -154,7 +154,7 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end) { - IOMMUTLBEvent event; + IOMMUTLBEvent event = {}; uint64_t delta = virt_end - virt_start; if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) { diff --git a/include/exec/memory.h b/include/exec/memory.h index 20f1b27377..c3180075e1 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -113,14 +113,48 @@ typedef enum { IOMMU_RW = 3, } IOMMUAccessFlags; +/* Granularity of the cache invalidation */ +typedef enum { + IOMMU_INV_GRAN_ADDR = 0, + IOMMU_INV_GRAN_PASID, + IOMMU_INV_GRAN_DOMAIN, +} IOMMUInvGranularity; + #define IOMMU_ACCESS_FLAG(r, w) (((r) ? IOMMU_RO : 0) | ((w) ? IOMMU_WO : 0)) +/** + * struct IOMMUTLBEntry - IOMMU TLB entry + * + * Structure used when performing a translation or when notifying MAP or + * UNMAP (invalidation) events + * + * @target_as: target address space + * @iova: IO virtual address (input) + * @translated_addr: translated address (output) + * @addr_mask: address mask (0xfff means 4K binding), must be multiple of 2 + * @perm: permission flag of the mapping (NONE encodes no mapping or + * invalidation notification) + * @granularity: granularity of the invalidation + * @flags: informs whether the following fields are set + * @arch_id: architecture specific ID tagging the TLB + * @pasid: PASID tagging the TLB + * @leaf: when @perm is NONE, indicates whether only caches for the last + * level of translation need to be invalidated. + */ struct IOMMUTLBEntry { AddressSpace *target_as; hwaddr iova; hwaddr translated_addr; - hwaddr addr_mask; /* 0xfff = 4k translation */ + hwaddr addr_mask; IOMMUAccessFlags perm; + IOMMUInvGranularity granularity; +#define IOMMU_INV_FLAGS_PASID (1 << 0) +#define IOMMU_INV_FLAGS_ARCHID (1 << 1) +#define IOMMU_INV_FLAGS_LEAF (1 << 2) + uint32_t flags; + uint32_t arch_id; + uint32_t pasid; + bool leaf; }; /* -- Gitee From de53feaa37a267a21ed30a642e1e64c5fcfbc4a4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Sun, 14 Feb 2021 12:30:57 -0500 Subject: [PATCH 091/486] hw/arm/smmuv3: Improve stage1 ASID invalidation At the moment ASID invalidation command (CMD_TLBI_NH_ASID) is propagated as a domain invalidation (the whole notifier range is invalidated independently on any ASID information). The new granularity field now allows to be more precise and restrict the invalidation to a peculiar ASID. Set the corresponding fields and flag. We still keep the iova and addr_mask settings for consumers that do not support the new fields, like VHOST. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- hw/arm/trace-events | 1 + 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 94e2c658f8..da5dac1ba5 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -836,6 +836,31 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, memory_region_notify_iommu_one(n, &event); } +/** + * smmuv3_notify_asid - call the notifier @n for a given asid + * + * @mr: IOMMU mr region handle + * @n: notifier to be called + * @asid: address space ID or negative value if we don't care + */ +static void smmuv3_notify_asid(IOMMUMemoryRegion *mr, + IOMMUNotifier *n, int asid) +{ + IOMMUTLBEvent event = {}; + + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.perm = IOMMU_NONE; + event.entry.granularity = IOMMU_INV_GRAN_PASID; + event.entry.flags = IOMMU_INV_FLAGS_ARCHID; + event.entry.arch_id = asid; + event.entry.iova = n->start; + event.entry.addr_mask = n->end - n->start; + + memory_region_notify_iommu_one(n, &event); +} + + /* invalidate an asid/iova range tuple in all mr's */ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova, uint8_t tg, uint64_t num_pages) @@ -913,6 +938,22 @@ smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data) return true; } +static void smmuv3_s1_asid_inval(SMMUState *s, uint16_t asid) +{ + SMMUDevice *sdev; + + trace_smmuv3_s1_asid_inval(asid); + QLIST_FOREACH(sdev, &s->devices_with_notifiers, next) { + IOMMUMemoryRegion *mr = &sdev->iommu; + IOMMUNotifier *n; + + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmuv3_notify_asid(mr, n, asid); + } + } + smmu_iotlb_inv_asid(s, asid); +} + static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUState *bs = ARM_SMMU(s); @@ -1027,8 +1068,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) uint16_t asid = CMD_ASID(&cmd); trace_smmuv3_cmdq_tlbi_nh_asid(asid); - smmu_inv_notifiers_all(&s->smmu_state); - smmu_iotlb_inv_asid(bs, asid); + smmuv3_s1_asid_inval(bs, asid); break; } case SMMU_CMD_TLBI_NH_ALL: diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 2dee296c8f..1447ad5a90 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -46,6 +46,7 @@ smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x" smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d" +smmuv3_s1_asid_inval(int asid) "asid=%d" smmuv3_cmdq_tlbi_nh(void) "" smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" -- Gitee From 876d18c962f0ead31d8458cd7ac19178be78455c Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 19 Mar 2021 12:22:48 -0400 Subject: [PATCH 092/486] hw/arm/smmu-common: Allow domain invalidation for NH_ALL/NSNH_ALL NH_ALL/NSNH_ALL corresponds to a domain granularity invalidation, ie. all the notifier range gets invalidation, whatever the ASID. So let's set the granularity to IOMMU_INV_GRAN_DOMAIN to allow the consumer to benefit from the info if it can. Signed-off-by: Eric Auger Suggested-by: chenxiang (M) Signed-off-by: Kunkun Jiang --- hw/arm/smmu-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3a1ecf81d6..2ec4222c93 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -477,6 +477,7 @@ static void smmu_unmap_notifier_range(IOMMUNotifier *n) event.entry.iova = n->start; event.entry.perm = IOMMU_NONE; event.entry.addr_mask = n->end - n->start; + event.entry.granularity = IOMMU_INV_GRAN_DOMAIN; memory_region_notify_iommu_one(n, &event); } -- Gitee From b380e3e0c30fb68dbbfb1397f3c374adfff77ac4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 1 Jul 2019 11:30:30 +0200 Subject: [PATCH 093/486] memory: Add IOMMU_ATTR_VFIO_NESTED IOMMU memory region attribute We introduce a new IOMMU Memory Region attribute, IOMMU_ATTR_VFIO_NESTED that tells whether the virtual IOMMU requires HW nested paging for VFIO integration. Current Intel virtual IOMMU device supports "Caching Mode" and does not require 2 stages at physical level to be integrated with VFIO. However SMMUv3 does not implement such "caching mode" and requires to use HW nested paging. As such SMMUv3 is the first IOMMU device to advertise this attribute. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 12 ++++++++++++ include/exec/memory.h | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index da5dac1ba5..9b87d16217 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1589,6 +1589,17 @@ static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, return 0; } +static int smmuv3_get_attr(IOMMUMemoryRegion *iommu, + enum IOMMUMemoryRegionAttr attr, + void *data) +{ + if (attr == IOMMU_ATTR_VFIO_NESTED) { + *(bool *) data = true; + return 0; + } + return -EINVAL; +} + static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, void *data) { @@ -1596,6 +1607,7 @@ static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = smmuv3_translate; imrc->notify_flag_changed = smmuv3_notify_flag_changed; + imrc->get_attr = smmuv3_get_attr; } static const TypeInfo smmuv3_type_info = { diff --git a/include/exec/memory.h b/include/exec/memory.h index c3180075e1..864bcaeb01 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -321,7 +321,8 @@ typedef struct MemoryRegionClass { enum IOMMUMemoryRegionAttr { - IOMMU_ATTR_SPAPR_TCE_FD + IOMMU_ATTR_SPAPR_TCE_FD, + IOMMU_ATTR_VFIO_NESTED, }; /* -- Gitee From 062923fd4e6d11e1b724f2dd059f8b0c6e65bf7a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 25 Mar 2019 16:35:05 +0100 Subject: [PATCH 094/486] memory: Add IOMMU_ATTR_MSI_TRANSLATE IOMMU memory region attribute We introduce a new IOMMU Memory Region attribute, IOMMU_ATTR_MSI_TRANSLATE which tells whether the virtual IOMMU translates MSIs. ARM SMMU will expose this attribute since, as opposed to Intel DMAR, MSIs are translated as any other DMA requests. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- include/exec/memory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index 864bcaeb01..76ef99ed27 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -323,6 +323,7 @@ typedef struct MemoryRegionClass { enum IOMMUMemoryRegionAttr { IOMMU_ATTR_SPAPR_TCE_FD, IOMMU_ATTR_VFIO_NESTED, + IOMMU_ATTR_MSI_TRANSLATE, }; /* -- Gitee From d2dce19165f133935ff72e209f19bc43ab4d1421 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 13 Sep 2018 14:13:04 +0200 Subject: [PATCH 095/486] memory: Introduce IOMMU Memory Region inject_faults API This new API allows to inject @count iommu_faults into the IOMMU memory region. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- include/exec/memory.h | 24 ++++++++++++++++++++++++ softmmu/memory.c | 10 ++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index 76ef99ed27..3e84d62e40 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -103,6 +103,8 @@ struct MemoryRegionSection { bool nonvolatile; }; +struct iommu_fault; + typedef struct IOMMUTLBEntry IOMMUTLBEntry; /* See address_space_translate: bit 0 is read, bit 1 is write. */ @@ -523,6 +525,19 @@ struct IOMMUMemoryRegionClass { int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, uint64_t page_size_mask, Error **errp); + + /* + * Inject @count faults into the IOMMU memory region + * + * Optional method: if this method is not provided, then + * memory_region_injection_faults() will return -ENOENT + * + * @iommu: the IOMMU memory region to inject the faults in + * @count: number of faults to inject + * @buf: fault buffer + */ + int (*inject_faults)(IOMMUMemoryRegion *iommu, int count, + struct iommu_fault *buf); }; typedef struct RamDiscardListener RamDiscardListener; @@ -1819,6 +1834,15 @@ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, uint64_t page_size_mask, Error **errp); +/** + * memory_region_inject_faults : inject @count faults stored in @buf + * + * @iommu_mr: the IOMMU memory region + * @count: number of faults to be injected + * @buf: buffer containing the faults + */ +int memory_region_inject_faults(IOMMUMemoryRegion *iommu_mr, int count, + struct iommu_fault *buf); /** * memory_region_name: get a memory region's name diff --git a/softmmu/memory.c b/softmmu/memory.c index 7340e19ff5..9f98209ab2 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -2111,6 +2111,16 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, rdmc->unregister_listener(rdm, rdl); } +int memory_region_inject_faults(IOMMUMemoryRegion *iommu_mr, int count, + struct iommu_fault *buf) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + if (!imrc->inject_faults) { + return -ENOENT; + } + return imrc->inject_faults(iommu_mr, count, buf); +} + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client; -- Gitee From 5e312f7b41ec48dc7dc9805af9f52aa8ed393bf9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 9 Jul 2019 12:20:12 +0200 Subject: [PATCH 096/486] iommu: Introduce generic header This header is meant to exposes data types used by several IOMMU devices such as struct for SVA and nested stage configuration. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- include/hw/iommu/iommu.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 include/hw/iommu/iommu.h diff --git a/include/hw/iommu/iommu.h b/include/hw/iommu/iommu.h new file mode 100644 index 0000000000..12092bda7b --- /dev/null +++ b/include/hw/iommu/iommu.h @@ -0,0 +1,28 @@ +/* + * common header for iommu devices + * + * Copyright Red Hat, Inc. 2019 + * + * Authors: + * Eric Auger + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef QEMU_HW_IOMMU_IOMMU_H +#define QEMU_HW_IOMMU_IOMMU_H +#ifdef __linux__ +#include +#endif + +typedef struct IOMMUConfig { + union { +#ifdef __linux__ + struct iommu_pasid_table_config pasid_cfg; +#endif + }; +} IOMMUConfig; + + +#endif /* QEMU_HW_IOMMU_IOMMU_H */ -- Gitee From c71485494970e7aa986be2b05bf7e2847017e264 Mon Sep 17 00:00:00 2001 From: Liu Yi L Date: Fri, 5 Jul 2019 19:01:36 +0800 Subject: [PATCH 097/486] pci: introduce PCIPASIDOps to PCIDevice This patch introduces PCIPASIDOps for IOMMU related operations. https://lists.gnu.org/archive/html/qemu-devel/2018-03/msg00078.html https://lists.gnu.org/archive/html/qemu-devel/2018-03/msg00940.html So far, to setup virt-SVA for assigned SVA capable device, needs to configure host translation structures for specific pasid. (e.g. bind guest page table to host and enable nested translation in host). Besides, vIOMMU emulator needs to forward guest's cache invalidation to host since host nested translation is enabled. e.g. on VT-d, guest owns 1st level translation table, thus cache invalidation for 1st level should be propagated to host. This patch adds two functions: alloc_pasid and free_pasid to support guest pasid allocation and free. The implementations of the callbacks would be device passthru modules. Like vfio. Cc: Kevin Tian Cc: Jacob Pan Cc: Peter Xu Cc: Eric Auger Cc: Yi Sun Cc: David Gibson Signed-off-by: Liu Yi L Signed-off-by: Yi Sun Signed-off-by: Kunkun Jiang --- hw/pci/pci.c | 34 ++++++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 11 +++++++++++ 2 files changed, 45 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e5993c1ef5..4a9374c025 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2759,6 +2759,40 @@ void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) bus->iommu_opaque = opaque; } +void pci_setup_pasid_ops(PCIDevice *dev, PCIPASIDOps *ops) +{ + assert(ops && !dev->pasid_ops); + dev->pasid_ops = ops; +} + +bool pci_device_is_pasid_ops_set(PCIBus *bus, int32_t devfn) +{ + PCIDevice *dev; + + if (!bus) { + return false; + } + + dev = bus->devices[devfn]; + return !!(dev && dev->pasid_ops); +} + +int pci_device_set_pasid_table(PCIBus *bus, int32_t devfn, + IOMMUConfig *config) +{ + PCIDevice *dev; + + if (!bus) { + return -EINVAL; + } + + dev = bus->devices[devfn]; + if (dev && dev->pasid_ops && dev->pasid_ops->set_pasid_table) { + return dev->pasid_ops->set_pasid_table(bus, devfn, config); + } + return -ENOENT; +} + static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) { Range *range = opaque; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index e7cdf2d5ec..abffa12a99 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -9,6 +9,7 @@ #include "hw/pci/pcie.h" #include "qom/object.h" +#include "hw/iommu/iommu.h" extern bool pci_available; @@ -265,6 +266,11 @@ struct PCIReqIDCache { }; typedef struct PCIReqIDCache PCIReqIDCache; +struct PCIPASIDOps { + int (*set_pasid_table)(PCIBus *bus, int32_t devfn, IOMMUConfig *config); +}; +typedef struct PCIPASIDOps PCIPASIDOps; + struct PCIDevice { DeviceState qdev; bool partially_hotplugged; @@ -361,6 +367,7 @@ struct PCIDevice { /* ID of standby device in net_failover pair */ char *failover_pair_id; uint32_t acpi_index; + PCIPASIDOps *pasid_ops; }; void pci_register_bar(PCIDevice *pci_dev, int region_num, @@ -498,6 +505,10 @@ typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int); AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque); +void pci_setup_pasid_ops(PCIDevice *dev, PCIPASIDOps *ops); +bool pci_device_is_pasid_ops_set(PCIBus *bus, int32_t devfn); +int pci_device_set_pasid_table(PCIBus *bus, int32_t devfn, IOMMUConfig *config); + static inline void pci_set_byte(uint8_t *config, uint8_t val) { -- Gitee From e7eef5af743a53f0415267ebe9bba2e5f0e05816 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 28 Aug 2018 16:16:20 +0200 Subject: [PATCH 098/486] vfio: Force nested if iommu requires it In case we detect the address space is translated by a virtual IOMMU which requires HW nested paging to integrate with VFIO, let's set up the container with the VFIO_TYPE1_NESTING_IOMMU iommu_type. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 6cb91e7ffd..d7533637c9 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -2045,27 +2045,38 @@ static void vfio_put_address_space(VFIOAddressSpace *space) * vfio_get_iommu_type - selects the richest iommu_type (v2 first) */ static int vfio_get_iommu_type(VFIOContainer *container, + bool want_nested, Error **errp) { - int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, + int iommu_types[] = { VFIO_TYPE1_NESTING_IOMMU, + VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, VFIO_SPAPR_TCE_v2_IOMMU, VFIO_SPAPR_TCE_IOMMU }; - int i; + int i, ret = -EINVAL; for (i = 0; i < ARRAY_SIZE(iommu_types); i++) { if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { - return iommu_types[i]; + if (iommu_types[i] == VFIO_TYPE1_NESTING_IOMMU && !want_nested) { + continue; + } + ret = iommu_types[i]; + break; } } - error_setg(errp, "No available IOMMU models"); - return -EINVAL; + if (ret < 0) { + error_setg(errp, "No available IOMMU models"); + } else if (want_nested && ret != VFIO_TYPE1_NESTING_IOMMU) { + error_setg(errp, "Nested mode requested but not supported"); + ret = -EINVAL; + } + return ret; } static int vfio_init_container(VFIOContainer *container, int group_fd, - Error **errp) + bool want_nested, Error **errp) { int iommu_type, dirty_log_manual_clear, ret; - iommu_type = vfio_get_iommu_type(container, errp); + iommu_type = vfio_get_iommu_type(container, want_nested, errp); if (iommu_type < 0) { return iommu_type; } @@ -2177,6 +2188,14 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, VFIOContainer *container; int ret, fd; VFIOAddressSpace *space; + IOMMUMemoryRegion *iommu_mr; + bool nested = false; + + if (memory_region_is_iommu(as->root)) { + iommu_mr = IOMMU_MEMORY_REGION(as->root); + memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_VFIO_NESTED, + (void *)&nested); + } space = vfio_get_address_space(as); @@ -2257,7 +2276,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, QLIST_INIT(&container->vrdl_list); QLIST_INIT(&container->dma_list); - ret = vfio_init_container(container, group->fd, errp); + ret = vfio_init_container(container, group->fd, nested, errp); if (ret) { goto free_container_exit; } @@ -2269,6 +2288,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, } switch (container->iommu_type) { + case VFIO_TYPE1_NESTING_IOMMU: case VFIO_TYPE1v2_IOMMU: case VFIO_TYPE1_IOMMU: { -- Gitee From 85232739b4852f1a51dde58c9007ed0deb17c2f2 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Mar 2019 18:05:23 +0100 Subject: [PATCH 099/486] vfio: Introduce hostwin_from_range helper Let's introduce a hostwin_from_range() helper that returns the hostwin encapsulating an IOVA range or NULL if none is found. This improves the readibility of callers and removes the usage of hostwin_found. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d7533637c9..d358789f19 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -909,6 +909,19 @@ static void vfio_unregister_ram_discard_listener(VFIOContainer *container, g_free(vrdl); } +static VFIOHostDMAWindow * +hostwin_from_range(VFIOContainer *container, hwaddr iova, hwaddr end) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { + return hostwin; + } + } + return NULL; +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -918,7 +931,6 @@ static void vfio_listener_region_add(MemoryListener *listener, void *vaddr; int ret; VFIOHostDMAWindow *hostwin; - bool hostwin_found; Error *err = NULL; if (vfio_listener_skipped_section(section)) { @@ -1011,15 +1023,8 @@ static void vfio_listener_region_add(MemoryListener *listener, #endif } - hostwin_found = false; - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { - hostwin_found = true; - break; - } - } - - if (!hostwin_found) { + hostwin = hostwin_from_range(container, iova, end); + if (!hostwin) { error_setg(&err, "Container %p can't map guest IOVA region" " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); goto fail; @@ -1211,16 +1216,9 @@ static void vfio_listener_region_del(MemoryListener *listener, if (memory_region_is_ram_device(section->mr)) { hwaddr pgmask; - VFIOHostDMAWindow *hostwin; - bool hostwin_found = false; + VFIOHostDMAWindow *hostwin = hostwin_from_range(container, iova, end); - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { - hostwin_found = true; - break; - } - } - assert(hostwin_found); /* or region_add() would have failed */ + assert(hostwin); /* or region_add() would have failed */ pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); -- Gitee From dab969657d8ff8b175856f91b035b74849cf69ba Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 30 Aug 2018 15:04:25 +0200 Subject: [PATCH 100/486] vfio: Introduce helpers to DMA map/unmap a RAM section Let's introduce two helpers that allow to DMA map/unmap a RAM section. Those helpers will be called for nested stage setup in another call site. Also the vfio_listener_region_add/del() structure may be clearer. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 206 +++++++++++++++++++++++++------------------ hw/vfio/trace-events | 4 +- 2 files changed, 123 insertions(+), 87 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d358789f19..b3dc090840 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -922,13 +922,130 @@ hostwin_from_range(VFIOContainer *container, hwaddr iova, hwaddr end) return NULL; } +static int vfio_dma_map_ram_section(VFIOContainer *container, + MemoryRegionSection *section, Error **err) +{ + VFIOHostDMAWindow *hostwin; + Int128 llend, llsize; + hwaddr iova, end; + void *vaddr; + int ret; + + assert(memory_region_is_ram(section->mr)); + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + end = int128_get64(int128_sub(llend, int128_one())); + + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + hostwin = hostwin_from_range(container, iova, end); + if (!hostwin) { + error_setg(err, "Container %p can't map guest IOVA region" + " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); + return -EFAULT; + } + + trace_vfio_dma_map_ram(iova, end, vaddr); + + llsize = int128_sub(llend, int128_make64(iova)); + + if (memory_region_is_ram_device(section->mr)) { + hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + + if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { + trace_vfio_listener_region_add_no_dma_map( + memory_region_name(section->mr), + section->offset_within_address_space, + int128_getlo(section->size), + pgmask + 1); + return 0; + } + } + + ret = vfio_dma_map(container, iova, int128_get64(llsize), + vaddr, section->readonly); + if (ret) { + error_setg(err, "vfio_dma_map(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx", %p) = %d (%m)", + container, iova, int128_get64(llsize), vaddr, ret); + if (memory_region_is_ram_device(section->mr)) { + /* Allow unexpected mappings not to be fatal for RAM devices */ + error_report_err(*err); + return 0; + } + return ret; + } + return 0; +} + +static void vfio_dma_unmap_ram_section(VFIOContainer *container, + MemoryRegionSection *section) +{ + Int128 llend, llsize; + hwaddr iova, end; + bool try_unmap = true; + int ret; + + iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); + llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); + + if (int128_ge(int128_make64(iova), llend)) { + return; + } + end = int128_get64(int128_sub(llend, int128_one())); + + llsize = int128_sub(llend, int128_make64(iova)); + + trace_vfio_dma_unmap_ram(iova, end); + + if (memory_region_is_ram_device(section->mr)) { + hwaddr pgmask; + VFIOHostDMAWindow *hostwin = hostwin_from_range(container, iova, end); + + assert(hostwin); /* or region_add() would have failed */ + + pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); + } else if (memory_region_has_ram_discard_manager(section->mr)) { + vfio_unregister_ram_discard_listener(container, section); + /* Unregistering will trigger an unmap. */ + try_unmap = false; + } + + if (try_unmap) { + if (int128_eq(llsize, int128_2_64())) { + /* The unmap ioctl doesn't accept a full 64-bit span. */ + llsize = int128_rshift(llsize, 1); + ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); + if (ret) { + error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%m)", + container, iova, int128_get64(llsize), ret); + } + iova += int128_get64(llsize); + } + ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); + if (ret) { + error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%m)", + container, iova, int128_get64(llsize), ret); + } + } +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { VFIOContainer *container = container_of(listener, VFIOContainer, listener); hwaddr iova, end; - Int128 llend, llsize; - void *vaddr; + Int128 llend; int ret; VFIOHostDMAWindow *hostwin; Error *err = NULL; @@ -1092,38 +1209,7 @@ static void vfio_listener_region_add(MemoryListener *listener, return; } - vaddr = memory_region_get_ram_ptr(section->mr) + - section->offset_within_region + - (iova - section->offset_within_address_space); - - trace_vfio_listener_region_add_ram(iova, end, vaddr); - - llsize = int128_sub(llend, int128_make64(iova)); - - if (memory_region_is_ram_device(section->mr)) { - hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; - - if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { - trace_vfio_listener_region_add_no_dma_map( - memory_region_name(section->mr), - section->offset_within_address_space, - int128_getlo(section->size), - pgmask + 1); - return; - } - } - - ret = vfio_dma_map(container, iova, int128_get64(llsize), - vaddr, section->readonly); - if (ret) { - error_setg(&err, "vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%m)", - container, iova, int128_get64(llsize), vaddr, ret); - if (memory_region_is_ram_device(section->mr)) { - /* Allow unexpected mappings not to be fatal for RAM devices */ - error_report_err(err); - return; - } + if (vfio_dma_map_ram_section(container, section, &err)) { goto fail; } @@ -1157,10 +1243,6 @@ static void vfio_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { VFIOContainer *container = container_of(listener, VFIOContainer, listener); - hwaddr iova, end; - Int128 llend, llsize; - int ret; - bool try_unmap = true; if (vfio_listener_skipped_section(section)) { trace_vfio_listener_region_del_skip( @@ -1200,53 +1282,7 @@ static void vfio_listener_region_del(MemoryListener *listener, */ } - iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); - - if (int128_ge(int128_make64(iova), llend)) { - return; - } - end = int128_get64(int128_sub(llend, int128_one())); - - llsize = int128_sub(llend, int128_make64(iova)); - - trace_vfio_listener_region_del(iova, end); - - if (memory_region_is_ram_device(section->mr)) { - hwaddr pgmask; - VFIOHostDMAWindow *hostwin = hostwin_from_range(container, iova, end); - - assert(hostwin); /* or region_add() would have failed */ - - pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; - try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); - } else if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_unregister_ram_discard_listener(container, section); - /* Unregistering will trigger an unmap. */ - try_unmap = false; - } - - if (try_unmap) { - if (int128_eq(llsize, int128_2_64())) { - /* The unmap ioctl doesn't accept a full 64-bit span. */ - llsize = int128_rshift(llsize, 1); - ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, int128_get64(llsize), ret); - } - iova += int128_get64(llsize); - } - ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, int128_get64(llsize), ret); - } - } + vfio_dma_unmap_ram_section(container, section); memory_region_unref(section->mr); diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 0ef1b5f4a6..a37563a315 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -99,10 +99,10 @@ vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "i vfio_listener_region_add_skip(uint64_t start, uint64_t end) "SKIPPING region_add 0x%"PRIx64" - 0x%"PRIx64 vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] 0x%"PRIx64" - 0x%"PRIx64 -vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]" +vfio_dma_map_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]" vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA" vfio_listener_region_del_skip(uint64_t start, uint64_t end) "SKIPPING region_del 0x%"PRIx64" - 0x%"PRIx64 -vfio_listener_region_del(uint64_t start, uint64_t end) "region_del 0x%"PRIx64" - 0x%"PRIx64 +vfio_dma_unmap_ram(uint64_t start, uint64_t end) "region_del 0x%"PRIx64" - 0x%"PRIx64 vfio_disconnect_container(int fd) "close container->fd=%d" vfio_put_group(int fd) "close group->fd=%d" vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" -- Gitee From 96581a5ee46e89dbc9e1ebe247b00adefb1c7a41 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 29 Aug 2018 18:10:12 +0200 Subject: [PATCH 101/486] vfio: Set up nested stage mappings In nested mode, legacy vfio_iommu_map_notify cannot be used as there is no "caching" mode and we do not trap on map. On Intel, vfio_iommu_map_notify was used to DMA map the RAM through the host single stage. With nested mode, we need to setup the stage 2 and the stage 1 separately. This patch introduces a prereg_listener to setup the stage 2 mapping. The stage 1 mapping, owned by the guest, is passed to the host when the guest invalidates the stage 1 configuration, through a dedicated PCIPASIDOps callback. Guest IOTLB invalidations are cascaded downto the host through another IOMMU MR UNMAP notifier. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 139 +++++++++++++++++++++++++++++++++++++++++-- hw/vfio/pci.c | 21 +++++++ hw/vfio/trace-events | 2 + 3 files changed, 157 insertions(+), 5 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index b3dc090840..58f8a43a43 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -707,6 +707,73 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, return true; } +/* Propagate a guest IOTLB invalidation to the host (nested mode) */ +static void vfio_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); + struct vfio_iommu_type1_cache_invalidate ustruct = {}; + VFIOContainer *container = giommu->container; + int ret; + + assert(iotlb->perm == IOMMU_NONE); + + ustruct.argsz = sizeof(ustruct); + ustruct.flags = 0; + ustruct.info.argsz = sizeof(struct iommu_cache_invalidate_info); + ustruct.info.version = IOMMU_CACHE_INVALIDATE_INFO_VERSION_1; + ustruct.info.cache = IOMMU_CACHE_INV_TYPE_IOTLB; + + switch (iotlb->granularity) { + case IOMMU_INV_GRAN_DOMAIN: + ustruct.info.granularity = IOMMU_INV_GRANU_DOMAIN; + break; + case IOMMU_INV_GRAN_PASID: + { + struct iommu_inv_pasid_info *pasid_info; + int archid = -1; + + pasid_info = &ustruct.info.granu.pasid_info; + ustruct.info.granularity = IOMMU_INV_GRANU_PASID; + if (iotlb->flags & IOMMU_INV_FLAGS_ARCHID) { + pasid_info->flags |= IOMMU_INV_ADDR_FLAGS_ARCHID; + archid = iotlb->arch_id; + } + pasid_info->archid = archid; + trace_vfio_iommu_asid_inv_iotlb(archid); + break; + } + case IOMMU_INV_GRAN_ADDR: + { + hwaddr start = iotlb->iova + giommu->iommu_offset; + struct iommu_inv_addr_info *addr_info; + size_t size = iotlb->addr_mask + 1; + int archid = -1; + + addr_info = &ustruct.info.granu.addr_info; + ustruct.info.granularity = IOMMU_INV_GRANU_ADDR; + if (iotlb->leaf) { + addr_info->flags |= IOMMU_INV_ADDR_FLAGS_LEAF; + } + if (iotlb->flags & IOMMU_INV_FLAGS_ARCHID) { + addr_info->flags |= IOMMU_INV_ADDR_FLAGS_ARCHID; + archid = iotlb->arch_id; + } + addr_info->archid = archid; + addr_info->addr = start; + addr_info->granule_size = size; + addr_info->nb_granules = 1; + trace_vfio_iommu_addr_inv_iotlb(archid, start, size, + 1, iotlb->leaf); + break; + } + } + + ret = ioctl(container->fd, VFIO_IOMMU_CACHE_INVALIDATE, &ustruct); + if (ret) { + error_report("%p: failed to invalidate CACHE (%d)", container, ret); + } +} + static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) { VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); @@ -1040,6 +1107,35 @@ static void vfio_dma_unmap_ram_section(VFIOContainer *container, } } +static void vfio_prereg_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = + container_of(listener, VFIOContainer, prereg_listener); + Error *err = NULL; + + if (!memory_region_is_ram(section->mr)) { + return; + } + + vfio_dma_map_ram_section(container, section, &err); + if (err) { + error_report_err(err); + } +} +static void vfio_prereg_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = + container_of(listener, VFIOContainer, prereg_listener); + + if (!memory_region_is_ram(section->mr)) { + return; + } + + vfio_dma_unmap_ram_section(container, section); +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -1150,9 +1246,10 @@ static void vfio_listener_region_add(MemoryListener *listener, memory_region_ref(section->mr); if (memory_region_is_iommu(section->mr)) { + IOMMUNotify notify; VFIOGuestIOMMU *giommu; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); - int iommu_idx; + int iommu_idx, flags; trace_vfio_listener_region_add_iommu(iova, end); /* @@ -1171,8 +1268,18 @@ static void vfio_listener_region_add(MemoryListener *listener, llend = int128_sub(llend, int128_one()); iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, MEMTXATTRS_UNSPECIFIED); - iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, - IOMMU_NOTIFIER_IOTLB_EVENTS, + + if (container->iommu_type == VFIO_TYPE1_NESTING_IOMMU) { + /* IOTLB unmap notifier to propagate guest IOTLB invalidations */ + flags = IOMMU_NOTIFIER_UNMAP; + notify = vfio_iommu_unmap_notify; + } else { + /* MAP/UNMAP IOTLB notifier */ + flags = IOMMU_NOTIFIER_IOTLB_EVENTS; + notify = vfio_iommu_map_notify; + } + + iommu_notifier_init(&giommu->n, notify, flags, section->offset_within_region, int128_get64(llend), iommu_idx); @@ -1192,7 +1299,9 @@ static void vfio_listener_region_add(MemoryListener *listener, goto fail; } QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); - memory_region_iommu_replay(giommu->iommu, &giommu->n); + if (flags & IOMMU_NOTIFIER_MAP) { + memory_region_iommu_replay(giommu->iommu, &giommu->n); + } return; } @@ -1672,10 +1781,16 @@ static const MemoryListener vfio_memory_listener = { .log_clear = vfio_listener_log_clear, }; +static MemoryListener vfio_memory_prereg_listener = { + .region_add = vfio_prereg_listener_region_add, + .region_del = vfio_prereg_listener_region_del, +}; + static void vfio_listener_release(VFIOContainer *container) { memory_listener_unregister(&container->listener); - if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU || + container->iommu_type == VFIO_TYPE1_NESTING_IOMMU) { memory_listener_unregister(&container->prereg_listener); } } @@ -2351,6 +2466,20 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, vfio_get_iommu_info_migration(container, info); } g_free(info); + + if (container->iommu_type == VFIO_TYPE1_NESTING_IOMMU) { + container->prereg_listener = vfio_memory_prereg_listener; + memory_listener_register(&container->prereg_listener, + &address_space_memory); + if (container->error) { + memory_listener_unregister(&container->prereg_listener); + ret = -1; + error_propagate_prepend(errp, container->error, + "RAM memory listener initialization failed " + "for container"); + goto free_container_exit; + } + } break; } case VFIO_SPAPR_TCE_v2_IOMMU: diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7b45353ce2..ae5e014e5d 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2797,6 +2797,25 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +static int vfio_iommu_set_pasid_table(PCIBus *bus, int32_t devfn, + IOMMUConfig *config) +{ + PCIDevice *pdev = bus->devices[devfn]; + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOContainer *container = vdev->vbasedev.group->container; + struct vfio_iommu_type1_set_pasid_table info; + + info.argsz = sizeof(info); + info.flags = VFIO_PASID_TABLE_FLAG_SET; + memcpy(&info.config, &config->pasid_cfg, sizeof(config->pasid_cfg)); + + return ioctl(container->fd, VFIO_IOMMU_SET_PASID_TABLE, &info); +} + +static PCIPASIDOps vfio_pci_pasid_ops = { + .set_pasid_table = vfio_iommu_set_pasid_table, +}; + static void vfio_realize(PCIDevice *pdev, Error **errp) { VFIOPCIDevice *vdev = VFIO_PCI(pdev); @@ -3108,6 +3127,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); + pci_setup_pasid_ops(pdev, &vfio_pci_pasid_ops); + return; out_deregister: diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index a37563a315..20069935f5 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -118,6 +118,8 @@ vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Devic vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" vfio_dma_unmap_overflow_workaround(void) "" +vfio_iommu_addr_inv_iotlb(int asid, uint64_t addr, uint64_t size, uint64_t nb_granules, bool leaf) "nested IOTLB invalidate asid=%d, addr=0x%"PRIx64" granule_size=0x%"PRIx64" nb_granules=0x%"PRIx64" leaf=%d" +vfio_iommu_asid_inv_iotlb(int asid) "nested IOTLB invalidate asid=%d" # platform.c vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d" -- Gitee From 8b4fbe869f8a1f510896c86067d2e4fc3dc82eb9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 14 Aug 2018 08:08:11 -0400 Subject: [PATCH 102/486] vfio: Pass stage 1 MSI bindings to the host We register the stage1 MSI bindings when enabling the vectors and we unregister them on msi disable. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 59 +++++++++++++++++++++++++++ hw/vfio/pci.c | 76 ++++++++++++++++++++++++++++++++++- hw/vfio/trace-events | 2 + include/hw/vfio/vfio-common.h | 12 ++++++ 4 files changed, 147 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 58f8a43a43..1f78af121d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -774,6 +774,65 @@ static void vfio_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) } } +int vfio_iommu_set_msi_binding(VFIOContainer *container, int n, + IOMMUTLBEntry *iotlb) +{ + struct vfio_iommu_type1_set_msi_binding ustruct; + VFIOMSIBinding *binding; + int ret; + + QLIST_FOREACH(binding, &container->msibinding_list, next) { + if (binding->index == n) { + return 0; + } + } + + ustruct.argsz = sizeof(struct vfio_iommu_type1_set_msi_binding); + ustruct.iova = iotlb->iova; + ustruct.flags = VFIO_IOMMU_BIND_MSI; + ustruct.gpa = iotlb->translated_addr; + ustruct.size = iotlb->addr_mask + 1; + ret = ioctl(container->fd, VFIO_IOMMU_SET_MSI_BINDING , &ustruct); + if (ret) { + error_report("%s: failed to register the stage1 MSI binding (%m)", + __func__); + return ret; + } + binding = g_new0(VFIOMSIBinding, 1); + binding->iova = ustruct.iova; + binding->gpa = ustruct.gpa; + binding->size = ustruct.size; + binding->index = n; + + QLIST_INSERT_HEAD(&container->msibinding_list, binding, next); + return 0; +} + +int vfio_iommu_unset_msi_binding(VFIOContainer *container, int n) +{ + struct vfio_iommu_type1_set_msi_binding ustruct; + VFIOMSIBinding *binding, *tmp; + int ret; + + ustruct.argsz = sizeof(struct vfio_iommu_type1_set_msi_binding); + QLIST_FOREACH_SAFE(binding, &container->msibinding_list, next, tmp) { + if (binding->index != n) { + continue; + } + ustruct.flags = VFIO_IOMMU_UNBIND_MSI; + ustruct.iova = binding->iova; + ret = ioctl(container->fd, VFIO_IOMMU_SET_MSI_BINDING , &ustruct); + if (ret) { + error_report("Failed to unregister the stage1 MSI binding " + "for iova=0x%"PRIx64" (%m)", binding->iova); + } + QLIST_REMOVE(binding, next); + g_free(binding); + return ret; + } + return 0; +} + static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) { VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index ae5e014e5d..99c52a0944 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -365,6 +365,65 @@ static void vfio_msi_interrupt(void *opaque) notify(&vdev->pdev, nr); } +static bool vfio_iommu_require_msi_binding(IOMMUMemoryRegion *iommu_mr) +{ + bool msi_translate = false, nested = false; + + memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_MSI_TRANSLATE, + (void *)&msi_translate); + memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_VFIO_NESTED, + (void *)&nested); + if (!nested || !msi_translate) { + return false; + } + return true; +} + +static int vfio_register_msi_binding(VFIOPCIDevice *vdev, + int vector_n, bool set) +{ + VFIOContainer *container = vdev->vbasedev.group->container; + PCIDevice *dev = &vdev->pdev; + AddressSpace *as = pci_device_iommu_address_space(dev); + IOMMUMemoryRegionClass *imrc; + IOMMUMemoryRegion *iommu_mr; + IOMMUTLBEntry entry; + MSIMessage msg; + + if (as == &address_space_memory) { + return 0; + } + + iommu_mr = IOMMU_MEMORY_REGION(as->root); + if (!vfio_iommu_require_msi_binding(iommu_mr)) { + return 0; + } + + /* MSI doorbell address is translated by an IOMMU */ + + if (!set) { /* unregister */ + trace_vfio_unregister_msi_binding(vdev->vbasedev.name, vector_n); + + return vfio_iommu_unset_msi_binding(container, vector_n); + } + + msg = pci_get_msi_message(dev, vector_n); + imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + + rcu_read_lock(); + entry = imrc->translate(iommu_mr, msg.address, IOMMU_WO, 0); + rcu_read_unlock(); + + if (entry.perm == IOMMU_NONE) { + return -ENOENT; + } + + trace_vfio_register_msi_binding(vdev->vbasedev.name, vector_n, + msg.address, entry.translated_addr); + + return vfio_iommu_set_msi_binding(container, vector_n, &entry); +} + static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) { struct vfio_irq_set *irq_set; @@ -382,7 +441,7 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) fds = (int32_t *)&irq_set->data; for (i = 0; i < vdev->nr_vectors; i++) { - int fd = -1; + int ret, fd = -1; /* * MSI vs MSI-X - The guest has direct access to MSI mask and pending @@ -391,6 +450,12 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) * KVM signaling path only when configured and unmasked. */ if (vdev->msi_vectors[i].use) { + ret = vfio_register_msi_binding(vdev, i, true); + if (ret) { + error_report("%s failed to register S1 MSI binding " + "for vector %d(%d)", vdev->vbasedev.name, i, ret); + goto out; + } if (vdev->msi_vectors[i].virq < 0 || (msix && msix_is_masked(&vdev->pdev, i))) { fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt); @@ -404,6 +469,7 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); +out: g_free(irq_set); return ret; @@ -718,7 +784,8 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) static void vfio_msix_disable(VFIOPCIDevice *vdev) { - int i; + int ret, i; + msix_unset_vector_notifiers(&vdev->pdev); @@ -730,6 +797,11 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) if (vdev->msi_vectors[i].use) { vfio_msix_vector_release(&vdev->pdev, i); msix_vector_unuse(&vdev->pdev, i); + ret = vfio_register_msi_binding(vdev, i, false); + if (ret) { + error_report("%s: failed to unregister S1 MSI binding " + "for vector %d(%d)", vdev->vbasedev.name, i, ret); + } } } diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 20069935f5..35bd415d6d 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -120,6 +120,8 @@ vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype vfio_dma_unmap_overflow_workaround(void) "" vfio_iommu_addr_inv_iotlb(int asid, uint64_t addr, uint64_t size, uint64_t nb_granules, bool leaf) "nested IOTLB invalidate asid=%d, addr=0x%"PRIx64" granule_size=0x%"PRIx64" nb_granules=0x%"PRIx64" leaf=%d" vfio_iommu_asid_inv_iotlb(int asid) "nested IOTLB invalidate asid=%d" +vfio_register_msi_binding(const char *name, int vector, uint64_t giova, uint64_t gdb) "%s: register vector %d gIOVA=0x%"PRIx64 "-> gDB=0x%"PRIx64" stage 1 mapping" +vfio_unregister_msi_binding(const char *name, int vector) "%s: unregister vector %d stage 1 mapping" # platform.c vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0234f5e1b1..a838a939e4 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -74,6 +74,14 @@ typedef struct VFIOAddressSpace { QLIST_ENTRY(VFIOAddressSpace) list; } VFIOAddressSpace; +typedef struct VFIOMSIBinding { + int index; + hwaddr iova; + hwaddr gpa; + hwaddr size; + QLIST_ENTRY(VFIOMSIBinding) next; +} VFIOMSIBinding; + struct VFIOGroup; typedef struct VFIODMARange { @@ -103,6 +111,7 @@ typedef struct VFIOContainer { QLIST_HEAD(, VFIOGroup) group_list; QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; QLIST_HEAD(, VFIODMARange) dma_list; + QLIST_HEAD(, VFIOMSIBinding) msibinding_list; QLIST_ENTRY(VFIOContainer) next; } VFIOContainer; @@ -222,6 +231,9 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp); void vfio_put_group(VFIOGroup *group); int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vbasedev, Error **errp); +int vfio_iommu_set_msi_binding(VFIOContainer *container, int n, + IOMMUTLBEntry *entry); +int vfio_iommu_unset_msi_binding(VFIOContainer *container, int n); extern const MemoryRegionOps vfio_region_ops; typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; -- Gitee From a4336765c99a876743c0ead89997ad6f97d7b442 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 20 Jun 2019 16:39:57 +0200 Subject: [PATCH 103/486] vfio: Helper to get IRQ info including capabilities As done for vfio regions, add helpers to retrieve irq info including their optional capabilities. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 97 +++++++++++++++++++++++++++++++++++ hw/vfio/trace-events | 1 + include/hw/vfio/vfio-common.h | 7 +++ 3 files changed, 105 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 1f78af121d..d05a485808 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1919,6 +1919,25 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, return true; } +struct vfio_info_cap_header * +vfio_get_irq_info_cap(struct vfio_irq_info *info, uint16_t id) +{ + struct vfio_info_cap_header *hdr; + void *ptr = info; + + if (!(info->flags & VFIO_IRQ_INFO_FLAG_CAPS)) { + return NULL; + } + + for (hdr = ptr + info->cap_offset; hdr != ptr; hdr = ptr + hdr->next) { + if (hdr->id == id) { + return hdr; + } + } + + return NULL; +} + static int vfio_setup_region_sparse_mmaps(VFIORegion *region, struct vfio_region_info *info) { @@ -2887,6 +2906,33 @@ retry: return 0; } +int vfio_get_irq_info(VFIODevice *vbasedev, int index, + struct vfio_irq_info **info) +{ + size_t argsz = sizeof(struct vfio_irq_info); + + *info = g_malloc0(argsz); + + (*info)->index = index; +retry: + (*info)->argsz = argsz; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, *info)) { + g_free(*info); + *info = NULL; + return -errno; + } + + if ((*info)->argsz > argsz) { + argsz = (*info)->argsz; + *info = g_realloc(*info, argsz); + + goto retry; + } + + return 0; +} + int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info) { @@ -2922,6 +2968,42 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, return -ENODEV; } +int vfio_get_dev_irq_info(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_irq_info **info) +{ + int i; + + for (i = 0; i < vbasedev->num_irqs; i++) { + struct vfio_info_cap_header *hdr; + struct vfio_irq_info_cap_type *cap_type; + + if (vfio_get_irq_info(vbasedev, i, info)) { + continue; + } + + hdr = vfio_get_irq_info_cap(*info, VFIO_IRQ_INFO_CAP_TYPE); + if (!hdr) { + g_free(*info); + continue; + } + + cap_type = container_of(hdr, struct vfio_irq_info_cap_type, header); + + trace_vfio_get_dev_irq(vbasedev->name, i, + cap_type->type, cap_type->subtype); + + if (cap_type->type == type && cap_type->subtype == subtype) { + return 0; + } + + g_free(*info); + } + + *info = NULL; + return -ENODEV; +} + + bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) { struct vfio_region_info *info = NULL; @@ -2937,6 +3019,21 @@ bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) return ret; } +bool vfio_has_irq_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +{ + struct vfio_region_info *info = NULL; + bool ret = false; + + if (!vfio_get_region_info(vbasedev, region, &info)) { + if (vfio_get_region_info_cap(info, cap_type)) { + ret = true; + } + g_free(info); + } + + return ret; +} + /* * Interfaces for IBM EEH (Enhanced Error Handling) */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 35bd415d6d..f5fe201ab5 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -117,6 +117,7 @@ vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Re vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" +vfio_get_dev_irq(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" vfio_dma_unmap_overflow_workaround(void) "" vfio_iommu_addr_inv_iotlb(int asid, uint64_t addr, uint64_t size, uint64_t nb_granules, bool leaf) "nested IOTLB invalidate asid=%d, addr=0x%"PRIx64" granule_size=0x%"PRIx64" nb_granules=0x%"PRIx64" leaf=%d" vfio_iommu_asid_inv_iotlb(int asid) "nested IOTLB invalidate asid=%d" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index a838a939e4..7fdca26fa0 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -254,6 +254,13 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, unsigned int *avail); struct vfio_info_cap_header * vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); +int vfio_get_irq_info(VFIODevice *vbasedev, int index, + struct vfio_irq_info **info); +int vfio_get_dev_irq_info(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_irq_info **info); +bool vfio_has_irq_cap(VFIODevice *vbasedev, int irq, uint16_t cap_type); +struct vfio_info_cap_header * +vfio_get_irq_info_cap(struct vfio_irq_info *info, uint16_t id); #endif extern const MemoryListener vfio_prereg_listener; -- Gitee From 574455d1363e818905e05cd23ef0948e83a16a51 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 13 Dec 2018 04:39:30 -0500 Subject: [PATCH 104/486] vfio/pci: Register handler for iommu fault We use the new extended IRQ VFIO_IRQ_TYPE_NESTED type and VFIO_IRQ_SUBTYPE_DMA_FAULT subtype to set/unset a notifier for physical DMA faults. The associated eventfd is triggered, in nested mode, whenever a fault is detected at IOMMU physical level. The actual handler will be implemented in subsequent patches. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/pci.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++- hw/vfio/pci.h | 7 +++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 99c52a0944..37a70932c6 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2888,6 +2888,76 @@ static PCIPASIDOps vfio_pci_pasid_ops = { .set_pasid_table = vfio_iommu_set_pasid_table, }; +static void vfio_dma_fault_notifier_handler(void *opaque) +{ + VFIOPCIExtIRQ *ext_irq = opaque; + + if (!event_notifier_test_and_clear(&ext_irq->notifier)) { + return; + } +} + +static int vfio_register_ext_irq_handler(VFIOPCIDevice *vdev, + uint32_t type, uint32_t subtype, + IOHandler *handler) +{ + int32_t fd, ext_irq_index, index; + struct vfio_irq_info *irq_info; + Error *err = NULL; + EventNotifier *n; + int ret; + + ret = vfio_get_dev_irq_info(&vdev->vbasedev, type, subtype, &irq_info); + if (ret) { + return ret; + } + index = irq_info->index; + ext_irq_index = irq_info->index - VFIO_PCI_NUM_IRQS; + g_free(irq_info); + + vdev->ext_irqs[ext_irq_index].vdev = vdev; + vdev->ext_irqs[ext_irq_index].index = index; + n = &vdev->ext_irqs[ext_irq_index].notifier; + + ret = event_notifier_init(n, 0); + if (ret) { + error_report("vfio: Unable to init event notifier for ext irq %d(%d)", + ext_irq_index, ret); + return ret; + } + + fd = event_notifier_get_fd(n); + qemu_set_fd_handler(fd, vfio_dma_fault_notifier_handler, NULL, + &vdev->ext_irqs[ext_irq_index]); + + ret = vfio_set_irq_signaling(&vdev->vbasedev, index, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err); + if (ret) { + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + qemu_set_fd_handler(fd, NULL, NULL, vdev); + event_notifier_cleanup(n); + } + return ret; +} + +static void vfio_unregister_ext_irq_notifiers(VFIOPCIDevice *vdev) +{ + VFIODevice *vbasedev = &vdev->vbasedev; + Error *err = NULL; + int i; + + for (i = 0; i < vbasedev->num_irqs - VFIO_PCI_NUM_IRQS; i++) { + if (vfio_set_irq_signaling(vbasedev, i + VFIO_PCI_NUM_IRQS , 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + } + qemu_set_fd_handler(event_notifier_get_fd(&vdev->ext_irqs[i].notifier), + NULL, NULL, vdev); + event_notifier_cleanup(&vdev->ext_irqs[i].notifier); + } + g_free(vdev->ext_irqs); +} + static void vfio_realize(PCIDevice *pdev, Error **errp) { VFIOPCIDevice *vdev = VFIO_PCI(pdev); @@ -2898,7 +2968,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) ssize_t len; struct stat st; int groupid; - int i, ret; + int i, ret, nb_ext_irqs; bool is_mdev; if (!vdev->vbasedev.sysfsdev) { @@ -2986,6 +3056,11 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto error; } + nb_ext_irqs = vdev->vbasedev.num_irqs - VFIO_PCI_NUM_IRQS; + if (nb_ext_irqs > 0) { + vdev->ext_irqs = g_new0(VFIOPCIExtIRQ, nb_ext_irqs); + } + vfio_populate_device(vdev, &err); if (err) { error_propagate(errp, err); @@ -3197,6 +3272,9 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_register_err_notifier(vdev); vfio_register_req_notifier(vdev); + vfio_register_ext_irq_handler(vdev, VFIO_IRQ_TYPE_NESTED, + VFIO_IRQ_SUBTYPE_DMA_FAULT, + vfio_dma_fault_notifier_handler); vfio_setup_resetfn_quirk(vdev); pci_setup_pasid_ops(pdev, &vfio_pci_pasid_ops); @@ -3239,6 +3317,7 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); + vfio_unregister_ext_irq_notifiers(vdev); pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 64777516d1..a8b06737fb 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -114,6 +114,12 @@ typedef struct VFIOMSIXInfo { unsigned long *pending; } VFIOMSIXInfo; +typedef struct VFIOPCIExtIRQ { + struct VFIOPCIDevice *vdev; + EventNotifier notifier; + uint32_t index; +} VFIOPCIExtIRQ; + #define TYPE_VFIO_PCI "vfio-pci" OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI) @@ -138,6 +144,7 @@ struct VFIOPCIDevice { PCIHostDeviceAddress host; EventNotifier err_notifier; EventNotifier req_notifier; + VFIOPCIExtIRQ *ext_irqs; int (*resetfn)(struct VFIOPCIDevice *); uint32_t vendor_id; uint32_t device_id; -- Gitee From e701d0fef4fbb7935d6aa7d22d82eb2dcfee2431 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 13 Dec 2018 10:57:53 -0500 Subject: [PATCH 105/486] vfio/pci: Set up the DMA FAULT region Set up the fault region which is composed of the actual fault queue (mmappable) and a header used to handle it. The fault queue is mmapped. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/pci.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.h | 1 + 2 files changed, 65 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 37a70932c6..76bc9d3506 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2638,11 +2638,67 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) return 0; } +static void vfio_init_fault_regions(VFIOPCIDevice *vdev, Error **errp) +{ + struct vfio_region_info *fault_region_info = NULL; + struct vfio_region_info_cap_fault *cap_fault; + VFIODevice *vbasedev = &vdev->vbasedev; + struct vfio_info_cap_header *hdr; + char *fault_region_name; + int ret; + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_NESTED, + VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT, + &fault_region_info); + if (ret) { + goto out; + } + + hdr = vfio_get_region_info_cap(fault_region_info, + VFIO_REGION_INFO_CAP_DMA_FAULT); + if (!hdr) { + error_setg(errp, "failed to retrieve DMA FAULT capability"); + goto out; + } + cap_fault = container_of(hdr, struct vfio_region_info_cap_fault, + header); + if (cap_fault->version != 1) { + error_setg(errp, "Unsupported DMA FAULT API version %d", + cap_fault->version); + goto out; + } + + fault_region_name = g_strdup_printf("%s DMA FAULT %d", + vbasedev->name, + fault_region_info->index); + + ret = vfio_region_setup(OBJECT(vdev), vbasedev, + &vdev->dma_fault_region, + fault_region_info->index, + fault_region_name); + g_free(fault_region_name); + if (ret) { + error_setg_errno(errp, -ret, + "failed to set up the DMA FAULT region %d", + fault_region_info->index); + goto out; + } + + ret = vfio_region_mmap(&vdev->dma_fault_region); + if (ret) { + error_setg_errno(errp, -ret, "Failed to mmap the DMA FAULT queue"); + } +out: + g_free(fault_region_info); +} + static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info *reg_info; struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; + Error *err = NULL; int i, ret = -1; /* Sanity check device */ @@ -2706,6 +2762,12 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) } } + vfio_init_fault_regions(vdev, &err); + if (err) { + error_propagate(errp, err); + return; + } + irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); @@ -3298,6 +3360,7 @@ static void vfio_instance_finalize(Object *obj) vfio_display_finalize(vdev); vfio_bars_finalize(vdev); + vfio_region_finalize(&vdev->dma_fault_region); g_free(vdev->emulated_config_bits); g_free(vdev->rom); /* @@ -3318,6 +3381,7 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); vfio_unregister_ext_irq_notifiers(vdev); + vfio_region_exit(&vdev->dma_fault_region); pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index a8b06737fb..eef91065f1 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -145,6 +145,7 @@ struct VFIOPCIDevice { EventNotifier err_notifier; EventNotifier req_notifier; VFIOPCIExtIRQ *ext_irqs; + VFIORegion dma_fault_region; int (*resetfn)(struct VFIOPCIDevice *); uint32_t vendor_id; uint32_t device_id; -- Gitee From d33cc7eccb68c6a1488804c94ff5c1197ee0fc6e Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 5 Mar 2019 16:35:32 +0100 Subject: [PATCH 106/486] vfio/pci: Implement the DMA fault handler Whenever the eventfd is triggered, we retrieve the DMA fault(s) from the mmapped fault region and inject them in the iommu memory region. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/pci.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.h | 1 + 2 files changed, 51 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 76bc9d3506..c54e62fe8f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2953,10 +2953,60 @@ static PCIPASIDOps vfio_pci_pasid_ops = { static void vfio_dma_fault_notifier_handler(void *opaque) { VFIOPCIExtIRQ *ext_irq = opaque; + VFIOPCIDevice *vdev = ext_irq->vdev; + PCIDevice *pdev = &vdev->pdev; + AddressSpace *as = pci_device_iommu_address_space(pdev); + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(as->root); + struct vfio_region_dma_fault header; + struct iommu_fault *queue; + char *queue_buffer = NULL; + ssize_t bytes; if (!event_notifier_test_and_clear(&ext_irq->notifier)) { return; } + + bytes = pread(vdev->vbasedev.fd, &header, sizeof(header), + vdev->dma_fault_region.fd_offset); + if (bytes != sizeof(header)) { + error_report("%s unable to read the fault region header (0x%lx)", + __func__, bytes); + return; + } + + /* Normally the fault queue is mmapped */ + queue = (struct iommu_fault *)vdev->dma_fault_region.mmaps[0].mmap; + if (!queue) { + size_t queue_size = header.nb_entries * header.entry_size; + + error_report("%s: fault queue not mmapped: slower fault handling", + vdev->vbasedev.name); + + queue_buffer = g_malloc(queue_size); + bytes = pread(vdev->vbasedev.fd, queue_buffer, queue_size, + vdev->dma_fault_region.fd_offset + header.offset); + if (bytes != queue_size) { + error_report("%s unable to read the fault queue (0x%lx)", + __func__, bytes); + return; + } + + queue = (struct iommu_fault *)queue_buffer; + } + + while (vdev->fault_tail_index != header.head) { + memory_region_inject_faults(iommu_mr, 1, + &queue[vdev->fault_tail_index]); + vdev->fault_tail_index = + (vdev->fault_tail_index + 1) % header.nb_entries; + } + bytes = pwrite(vdev->vbasedev.fd, &vdev->fault_tail_index, 4, + vdev->dma_fault_region.fd_offset); + if (bytes != 4) { + error_report("%s unable to write the fault region tail index (0x%lx)", + __func__, bytes); + } + g_free(queue_buffer); } static int vfio_register_ext_irq_handler(VFIOPCIDevice *vdev, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index eef91065f1..03ac8919ef 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -146,6 +146,7 @@ struct VFIOPCIDevice { EventNotifier req_notifier; VFIOPCIExtIRQ *ext_irqs; VFIORegion dma_fault_region; + uint32_t fault_tail_index; int (*resetfn)(struct VFIOPCIDevice *); uint32_t vendor_id; uint32_t device_id; -- Gitee From 5a759ab19d508361053e388694546216705d173b Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 28 Aug 2018 09:21:53 -0400 Subject: [PATCH 107/486] hw/arm/smmuv3: Advertise MSI_TRANSLATE attribute The SMMUv3 has the peculiarity to translate MSI transactionss. let's advertise the corresponding attribute. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 9b87d16217..12f354a0d5 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1596,6 +1596,9 @@ static int smmuv3_get_attr(IOMMUMemoryRegion *iommu, if (attr == IOMMU_ATTR_VFIO_NESTED) { *(bool *) data = true; return 0; + } else if (attr == IOMMU_ATTR_MSI_TRANSLATE) { + *(bool *) data = true; + return 0; } return -EINVAL; } -- Gitee From f937ce4124d57eea27d516957a2efa0e7fbdf198 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 9 Aug 2018 20:56:44 +0200 Subject: [PATCH 108/486] hw/arm/smmuv3: Store the PASID table GPA in the translation config For VFIO integration we will need to pass the Context Descriptor (CD) table GPA to the host. The CD table is also referred to as the PASID table. Its GPA corresponds to the s1ctrptr field of the Stream Table Entry. So let's decode and store it in the configuration structure. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 1 + include/hw/arm/smmu-common.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 12f354a0d5..3416f6a639 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -358,6 +358,7 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, "SMMUv3 S1 stalling fault model not allowed yet\n"); goto bad_ste; } + cfg->s1ctxptr = STE_CTXPTR(ste); return 0; bad_ste: diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 706be3c6d0..d578339935 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -76,6 +76,7 @@ typedef struct SMMUTransCfg { uint8_t tbi; /* Top Byte Ignore */ uint16_t asid; SMMUTransTableInfo tt[2]; + dma_addr_t s1ctxptr; uint32_t iotlb_hits; /* counts IOTLB hits for this asid */ uint32_t iotlb_misses; /* counts IOTLB misses for this asid */ } SMMUTransCfg; -- Gitee From dcda615b3d9b1acffee3d31d57974cc9e4bd0dee Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 4 Sep 2018 08:48:33 -0400 Subject: [PATCH 109/486] hw/arm/smmuv3: Fill the IOTLBEntry arch_id on NH_VA invalidation When the guest invalidates one S1 entry, it passes the asid. When propagating this invalidation downto the host, the asid information also must be passed. So let's fill the arch_id field introduced for that purpose and accordingly set the flags to indicate its presence. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 3416f6a639..696c588f08 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -833,6 +833,8 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, event.entry.iova = iova; event.entry.addr_mask = num_pages * (1 << granule) - 1; event.entry.perm = IOMMU_NONE; + event.entry.flags = IOMMU_INV_FLAGS_ARCHID; + event.entry.arch_id = asid; memory_region_notify_iommu_one(n, &event); } -- Gitee From c219274b7b6a472d7340a4f72a052ba33ed19659 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 14 Mar 2019 09:55:13 -0400 Subject: [PATCH 110/486] hw/arm/smmuv3: Fill the IOTLBEntry leaf field on NH_VA invalidation Let's propagate the leaf attribute throughout the invalidation path. This hint is used to reduce the scope of the invalidations to the last level of translation. Not enforcing it induces large performance penalties in nested mode. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 696c588f08..ad816e850c 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -800,7 +800,7 @@ epilogue: static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, IOMMUNotifier *n, int asid, dma_addr_t iova, - uint8_t tg, uint64_t num_pages) + uint8_t tg, uint64_t num_pages, bool leaf) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); IOMMUTLBEvent event = {}; @@ -835,6 +835,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, event.entry.perm = IOMMU_NONE; event.entry.flags = IOMMU_INV_FLAGS_ARCHID; event.entry.arch_id = asid; + event.entry.leaf = leaf; memory_region_notify_iommu_one(n, &event); } @@ -866,7 +867,7 @@ static void smmuv3_notify_asid(IOMMUMemoryRegion *mr, /* invalidate an asid/iova range tuple in all mr's */ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova, - uint8_t tg, uint64_t num_pages) + uint8_t tg, uint64_t num_pages, bool leaf) { SMMUDevice *sdev; @@ -878,7 +879,7 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova, tg, num_pages); IOMMU_NOTIFIER_FOREACH(n, mr) { - smmuv3_notify_iova(mr, n, asid, iova, tg, num_pages); + smmuv3_notify_iova(mr, n, asid, iova, tg, num_pages, leaf); } } } @@ -903,7 +904,7 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) if (!tg) { trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, 1, ttl, leaf); - smmuv3_inv_notifiers_iova(s, asid, addr, tg, 1); + smmuv3_inv_notifiers_iova(s, asid, addr, tg, 1, leaf); smmu_iotlb_inv_iova(s, asid, addr, tg, 1, ttl); return; } @@ -921,7 +922,7 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) num_pages = (mask + 1) >> granule; trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf); - smmuv3_inv_notifiers_iova(s, asid, addr, tg, num_pages); + smmuv3_inv_notifiers_iova(s, asid, addr, tg, num_pages, leaf); smmu_iotlb_inv_iova(s, asid, addr, tg, num_pages, ttl); addr += mask + 1; } -- Gitee From 2e5929ec2a35a7a227dc7ba70a557a84993a366d Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 9 Aug 2018 21:04:19 +0200 Subject: [PATCH 111/486] hw/arm/smmuv3: Pass stage 1 configurations to the host In case PASID PciOps are set for the device we call the set_pasid_table() callback on each STE update. This allows to pass the guest stage 1 configuration to the host and apply it at physical level. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmu-internal.h | 1 + hw/arm/smmuv3.c | 71 ++++++++++++++++++++++++++++++++++++------ hw/arm/trace-events | 1 + 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h index 2d75b31953..5ef8c598c6 100644 --- a/hw/arm/smmu-internal.h +++ b/hw/arm/smmu-internal.h @@ -105,6 +105,7 @@ typedef struct SMMUIOTLBPageInvInfo { } SMMUIOTLBPageInvInfo; typedef struct SMMUSIDRange { + SMMUState *state; uint32_t start; uint32_t end; } SMMUSIDRange; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index ad816e850c..58139f707d 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -16,6 +16,10 @@ * with this program; if not, see . */ +#ifdef __linux__ +#include "linux/iommu.h" +#endif + #include "qemu/osdep.h" #include "qemu/bitops.h" #include "hw/irq.h" @@ -928,6 +932,61 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) } } +static void smmuv3_notify_config_change(SMMUState *bs, uint32_t sid) +{ +#ifdef __linux__ + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); + SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid, + .inval_ste_allowed = true}; + IOMMUConfig iommu_config = {}; + SMMUTransCfg *cfg; + SMMUDevice *sdev; + + if (!mr) { + return; + } + + sdev = container_of(mr, SMMUDevice, iommu); + + /* flush QEMU config cache */ + smmuv3_flush_config(sdev); + + if (!pci_device_is_pasid_ops_set(sdev->bus, sdev->devfn)) { + return; + } + + cfg = smmuv3_get_config(sdev, &event); + + if (!cfg) { + return; + } + + iommu_config.pasid_cfg.argsz = sizeof(struct iommu_pasid_table_config); + iommu_config.pasid_cfg.version = PASID_TABLE_CFG_VERSION_1; + iommu_config.pasid_cfg.format = IOMMU_PASID_FORMAT_SMMUV3; + iommu_config.pasid_cfg.base_ptr = cfg->s1ctxptr; + iommu_config.pasid_cfg.pasid_bits = 0; + iommu_config.pasid_cfg.vendor_data.smmuv3.version = PASID_TABLE_SMMUV3_CFG_VERSION_1; + + if (cfg->disabled || cfg->bypassed) { + iommu_config.pasid_cfg.config = IOMMU_PASID_CONFIG_BYPASS; + } else if (cfg->aborted) { + iommu_config.pasid_cfg.config = IOMMU_PASID_CONFIG_ABORT; + } else { + iommu_config.pasid_cfg.config = IOMMU_PASID_CONFIG_TRANSLATE; + } + + trace_smmuv3_notify_config_change(mr->parent_obj.name, + iommu_config.pasid_cfg.config, + iommu_config.pasid_cfg.base_ptr); + + if (pci_device_set_pasid_table(sdev->bus, sdev->devfn, &iommu_config)) { + error_report("Failed to pass PASID table to host for iommu mr %s (%m)", + mr->parent_obj.name); + } +#endif +} + static gboolean smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data) { @@ -938,6 +997,7 @@ smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data) if (sid < sid_range->start || sid > sid_range->end) { return false; } + smmuv3_notify_config_change(sid_range->state, sid); trace_smmuv3_config_cache_inv(sid); return true; } @@ -1008,22 +1068,14 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_CFGI_STE: { uint32_t sid = CMD_SID(&cmd); - IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); - SMMUDevice *sdev; if (CMD_SSEC(&cmd)) { cmd_error = SMMU_CERROR_ILL; break; } - if (!mr) { - break; - } - trace_smmuv3_cmdq_cfgi_ste(sid); - sdev = container_of(mr, SMMUDevice, iommu); - smmuv3_flush_config(sdev); - + smmuv3_notify_config_change(bs, sid); break; } case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */ @@ -1038,6 +1090,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) } mask = (1ULL << (range + 1)) - 1; + sid_range.state = bs; sid_range.start = sid & ~mask; sid_range.end = sid_range.start + mask; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 1447ad5a90..d9851d663e 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -53,4 +53,5 @@ smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 +smmuv3_notify_config_change(const char *name, uint8_t config, uint64_t s1ctxptr) "iommu mr=%s config=%d s1ctxptr=0x%"PRIx64 -- Gitee From d31c754470b4b651d0e19c66738fbcc8fc6abf3c Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Thu, 13 Sep 2018 14:24:45 +0200 Subject: [PATCH 112/486] hw/arm/smmuv3: Implement fault injection We convert iommu_fault structs received from the kernel into the data struct used by the emulation code and record the evnts into the virtual event queue. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 58139f707d..9aeb420428 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1660,6 +1660,76 @@ static int smmuv3_get_attr(IOMMUMemoryRegion *iommu, return -EINVAL; } +struct iommu_fault; + +static inline int +smmuv3_inject_faults(IOMMUMemoryRegion *iommu_mr, int count, + struct iommu_fault *buf) +{ +#ifdef __linux__ + SMMUDevice *sdev = container_of(iommu_mr, SMMUDevice, iommu); + SMMUv3State *s3 = sdev->smmu; + uint32_t sid = smmu_get_sid(sdev); + int i; + + for (i = 0; i < count; i++) { + SMMUEventInfo info = {}; + struct iommu_fault_unrecoverable *record; + + if (buf[i].type != IOMMU_FAULT_DMA_UNRECOV) { + continue; + } + + info.sid = sid; + record = &buf[i].event; + + switch (record->reason) { + case IOMMU_FAULT_REASON_PASID_INVALID: + info.type = SMMU_EVT_C_BAD_SUBSTREAMID; + /* TODO further fill info.u.c_bad_substream */ + break; + case IOMMU_FAULT_REASON_PASID_FETCH: + info.type = SMMU_EVT_F_CD_FETCH; + break; + case IOMMU_FAULT_REASON_BAD_PASID_ENTRY: + info.type = SMMU_EVT_C_BAD_CD; + /* TODO further fill info.u.c_bad_cd */ + break; + case IOMMU_FAULT_REASON_WALK_EABT: + info.type = SMMU_EVT_F_WALK_EABT; + info.u.f_walk_eabt.addr = record->addr; + info.u.f_walk_eabt.addr2 = record->fetch_addr; + break; + case IOMMU_FAULT_REASON_PTE_FETCH: + info.type = SMMU_EVT_F_TRANSLATION; + info.u.f_translation.addr = record->addr; + break; + case IOMMU_FAULT_REASON_OOR_ADDRESS: + info.type = SMMU_EVT_F_ADDR_SIZE; + info.u.f_addr_size.addr = record->addr; + break; + case IOMMU_FAULT_REASON_ACCESS: + info.type = SMMU_EVT_F_ACCESS; + info.u.f_access.addr = record->addr; + break; + case IOMMU_FAULT_REASON_PERMISSION: + info.type = SMMU_EVT_F_PERMISSION; + info.u.f_permission.addr = record->addr; + break; + default: + warn_report("%s Unexpected fault reason received from host: %d", + __func__, record->reason); + continue; + } + + smmuv3_record_event(s3, &info); + } + return 0; +#else + return -1; +#endif +} + static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, void *data) { @@ -1668,6 +1738,7 @@ static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = smmuv3_translate; imrc->notify_flag_changed = smmuv3_notify_flag_changed; imrc->get_attr = smmuv3_get_attr; + imrc->inject_faults = smmuv3_inject_faults; } static const TypeInfo smmuv3_type_info = { -- Gitee From dc126664134989975ce9ab9e7d5d2c8916628bf6 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 18 Mar 2020 11:17:36 +0100 Subject: [PATCH 113/486] hw/arm/smmuv3: Allow MAP notifiers We now have all bricks to support nested paging. This uses MAP notifiers to map the MSIs. So let's allow MAP notifiers to be registered. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 9aeb420428..45f21c53fe 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1628,14 +1628,6 @@ static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, return -EINVAL; } - if (new & IOMMU_NOTIFIER_MAP) { - error_setg(errp, - "device %02x.%02x.%x requires iommu MAP notifier which is " - "not currently supported", pci_bus_num(sdev->bus), - PCI_SLOT(sdev->devfn), PCI_FUNC(sdev->devfn)); - return -EINVAL; - } - if (old == IOMMU_NOTIFIER_NONE) { trace_smmuv3_notify_flag_add(iommu->parent_obj.name); QLIST_INSERT_HEAD(&s->devices_with_notifiers, sdev, next); -- Gitee From 228345cfa59c764e725e2d3680a4bc3ecb237609 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 6 Nov 2020 14:34:35 +0100 Subject: [PATCH 114/486] pci: Add return_page_response pci ops Add a new PCI operation that allows to return page responses to registered VFIO devices Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/pci/pci.c | 16 ++++++++++++++++ include/hw/iommu/iommu.h | 8 ++++++++ include/hw/pci/pci.h | 4 ++++ 3 files changed, 28 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 4a9374c025..64db325d6b 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2793,6 +2793,22 @@ int pci_device_set_pasid_table(PCIBus *bus, int32_t devfn, return -ENOENT; } +int pci_device_return_page_response(PCIBus *bus, int32_t devfn, + IOMMUPageResponse *resp) +{ + PCIDevice *dev; + + if (!bus) { + return -EINVAL; + } + + dev = bus->devices[devfn]; + if (dev && dev->pasid_ops && dev->pasid_ops->return_page_response) { + return dev->pasid_ops->return_page_response(bus, devfn, resp); + } + return -ENOENT; +} + static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) { Range *range = opaque; diff --git a/include/hw/iommu/iommu.h b/include/hw/iommu/iommu.h index 12092bda7b..5890f095b1 100644 --- a/include/hw/iommu/iommu.h +++ b/include/hw/iommu/iommu.h @@ -24,5 +24,13 @@ typedef struct IOMMUConfig { }; } IOMMUConfig; +typedef struct IOMMUPageResponse { + union { +#ifdef __linux__ + struct iommu_page_response resp; +#endif + }; +} IOMMUPageResponse; + #endif /* QEMU_HW_IOMMU_IOMMU_H */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index abffa12a99..809eb32f4a 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -268,6 +268,8 @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIPASIDOps { int (*set_pasid_table)(PCIBus *bus, int32_t devfn, IOMMUConfig *config); + int (*return_page_response)(PCIBus *bus, int32_t devfn, + IOMMUPageResponse *resp); }; typedef struct PCIPASIDOps PCIPASIDOps; @@ -508,6 +510,8 @@ void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque); void pci_setup_pasid_ops(PCIDevice *dev, PCIPASIDOps *ops); bool pci_device_is_pasid_ops_set(PCIBus *bus, int32_t devfn); int pci_device_set_pasid_table(PCIBus *bus, int32_t devfn, IOMMUConfig *config); +int pci_device_return_page_response(PCIBus *bus, int32_t devfn, + IOMMUPageResponse *resp); static inline void pci_set_byte(uint8_t *config, uint8_t val) -- Gitee From 6bbf810edebdb89a6958519ee3adfb1888520231 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 6 Nov 2020 12:03:29 -0500 Subject: [PATCH 115/486] vfio/pci: Implement return_page_response page response callback This patch implements the page response path. The response is written into the page response ring buffer and then update header's head index is updated. This path is not used by this series. It is introduced here as a POC for vSVA/ARM integration. Signed-off-by: Eric Auger Signed-off-by: Kunkun Jiang --- hw/vfio/pci.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.h | 2 + 2 files changed, 125 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c54e62fe8f..8e24f9c7d1 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2693,6 +2693,61 @@ out: g_free(fault_region_info); } +static void vfio_init_fault_response_regions(VFIOPCIDevice *vdev, Error **errp) +{ + struct vfio_region_info *fault_region_info = NULL; + struct vfio_region_info_cap_fault *cap_fault; + VFIODevice *vbasedev = &vdev->vbasedev; + struct vfio_info_cap_header *hdr; + char *fault_region_name; + int ret; + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_NESTED, + VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT_RESPONSE, + &fault_region_info); + if (ret) { + goto out; + } + + hdr = vfio_get_region_info_cap(fault_region_info, + VFIO_REGION_INFO_CAP_DMA_FAULT_RESPONSE); + if (!hdr) { + error_setg(errp, "failed to retrieve DMA FAULT RESPONSE capability"); + goto out; + } + cap_fault = container_of(hdr, struct vfio_region_info_cap_fault, + header); + if (cap_fault->version != 1) { + error_setg(errp, "Unsupported DMA FAULT RESPONSE API version %d", + cap_fault->version); + goto out; + } + + fault_region_name = g_strdup_printf("%s DMA FAULT RESPONSE %d", + vbasedev->name, + fault_region_info->index); + + ret = vfio_region_setup(OBJECT(vdev), vbasedev, + &vdev->dma_fault_response_region, + fault_region_info->index, + fault_region_name); + g_free(fault_region_name); + if (ret) { + error_setg_errno(errp, -ret, + "failed to set up the DMA FAULT RESPONSE region %d", + fault_region_info->index); + goto out; + } + + ret = vfio_region_mmap(&vdev->dma_fault_response_region); + if (ret) { + error_setg_errno(errp, -ret, "Failed to mmap the DMA FAULT RESPONSE queue"); + } +out: + g_free(fault_region_info); +} + static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; @@ -2768,6 +2823,12 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) return; } + vfio_init_fault_response_regions(vdev, &err); + if (err) { + error_propagate(errp, err); + return; + } + irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); @@ -2946,8 +3007,68 @@ static int vfio_iommu_set_pasid_table(PCIBus *bus, int32_t devfn, return ioctl(container->fd, VFIO_IOMMU_SET_PASID_TABLE, &info); } +static int vfio_iommu_return_page_response(PCIBus *bus, int32_t devfn, + IOMMUPageResponse *resp) +{ + PCIDevice *pdev = bus->devices[devfn]; + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + struct iommu_page_response *response = &resp->resp; + struct vfio_region_dma_fault_response header; + struct iommu_page_response *queue; + char *queue_buffer = NULL; + ssize_t bytes; + + if (!vdev->dma_fault_response_region.mem) { + return -EINVAL; + } + + /* read the header */ + bytes = pread(vdev->vbasedev.fd, &header, sizeof(header), + vdev->dma_fault_response_region.fd_offset); + if (bytes != sizeof(header)) { + error_report("%s unable to read the fault region header (0x%lx)", + __func__, bytes); + return -1; + } + + /* Normally the fault queue is mmapped */ + queue = (struct iommu_page_response *)vdev->dma_fault_response_region.mmaps[0].mmap; + if (!queue) { + size_t queue_size = header.nb_entries * header.entry_size; + + error_report("%s: fault queue not mmapped: slower fault handling", + vdev->vbasedev.name); + + queue_buffer = g_malloc(queue_size); + bytes = pread(vdev->vbasedev.fd, queue_buffer, queue_size, + vdev->dma_fault_response_region.fd_offset + header.offset); + if (bytes != queue_size) { + error_report("%s unable to read the fault queue (0x%lx)", + __func__, bytes); + return -1; + } + + queue = (struct iommu_page_response *)queue_buffer; + } + /* deposit the new response in the queue and increment the head */ + memcpy(queue + header.head, response, header.entry_size); + + vdev->fault_response_head_index = + (vdev->fault_response_head_index + 1) % header.nb_entries; + bytes = pwrite(vdev->vbasedev.fd, &vdev->fault_response_head_index, 4, + vdev->dma_fault_response_region.fd_offset); + if (bytes != 4) { + error_report("%s unable to write the fault response region head index (0x%lx)", + __func__, bytes); + } + g_free(queue_buffer); + + return 0; +} + static PCIPASIDOps vfio_pci_pasid_ops = { .set_pasid_table = vfio_iommu_set_pasid_table, + .return_page_response = vfio_iommu_return_page_response, }; static void vfio_dma_fault_notifier_handler(void *opaque) @@ -3411,6 +3532,7 @@ static void vfio_instance_finalize(Object *obj) vfio_display_finalize(vdev); vfio_bars_finalize(vdev); vfio_region_finalize(&vdev->dma_fault_region); + vfio_region_finalize(&vdev->dma_fault_response_region); g_free(vdev->emulated_config_bits); g_free(vdev->rom); /* @@ -3432,6 +3554,7 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_unregister_err_notifier(vdev); vfio_unregister_ext_irq_notifiers(vdev); vfio_region_exit(&vdev->dma_fault_region); + vfio_region_exit(&vdev->dma_fault_response_region); pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 03ac8919ef..61b3bf1303 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -147,6 +147,8 @@ struct VFIOPCIDevice { VFIOPCIExtIRQ *ext_irqs; VFIORegion dma_fault_region; uint32_t fault_tail_index; + VFIORegion dma_fault_response_region; + uint32_t fault_response_head_index; int (*resetfn)(struct VFIOPCIDevice *); uint32_t vendor_id; uint32_t device_id; -- Gitee From 9d7b782a0b2c5288e82f3064b4c5b7bf18887280 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Sat, 31 Jul 2021 10:02:18 +0800 Subject: [PATCH 116/486] vfio/common: Avoid unmap ram section at vfio_listener_region_del() in nested mode The ram section will be unmapped at vfio_prereg_listener_region_del() in nested mode. So let's avoid unmap ram section at vfio_listener_region_dev(). Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d05a485808..bdfcc854fe 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1441,6 +1441,16 @@ static void vfio_listener_region_del(MemoryListener *listener, } } + /* + * In nested mode, stage 2 (gpa->hpa) and the stage 1 + * (giova->gpa) are set separately. The ram section + * will be unmapped in vfio_prereg_listener_region_del(). + * Hence it doesn't need to unmap ram section here. + */ + if (container->iommu_type == VFIO_TYPE1_NESTING_IOMMU) { + return; + } + /* * FIXME: We assume the one big unmap below is adequate to * remove any individual page mappings in the IOMMU which -- Gitee From 1675d767aa9bd496178b4d74e01a40dbbd97eccb Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Tue, 11 May 2021 10:08:13 +0800 Subject: [PATCH 117/486] vfio: Introduce helpers to mark dirty pages of a RAM section Extract part of the code from vfio_sync_dirty_bitmap to form a new helper, which allows to mark dirty pages of a RAM section. This helper will be called for nested stage. Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index bdfcc854fe..6136b1ef61 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1566,6 +1566,19 @@ err_out: return ret; } +static int vfio_dma_sync_ram_section_dirty_bitmap(VFIOContainer *container, + MemoryRegionSection *section) +{ + ram_addr_t ram_addr; + + ram_addr = memory_region_get_ram_addr(section->mr) + + section->offset_within_region; + + return vfio_get_dirty_bitmap(container, + REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), + int128_get64(section->size), ram_addr); +} + typedef struct { IOMMUNotifier n; VFIOGuestIOMMU *giommu; @@ -1650,8 +1663,6 @@ static int vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *container, static int vfio_sync_dirty_bitmap(VFIOContainer *container, MemoryRegionSection *section) { - ram_addr_t ram_addr; - if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; @@ -1682,12 +1693,7 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, return vfio_sync_ram_discard_listener_dirty_bitmap(container, section); } - ram_addr = memory_region_get_ram_addr(section->mr) + - section->offset_within_region; - - return vfio_get_dirty_bitmap(container, - REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), - int128_get64(section->size), ram_addr); + return vfio_dma_sync_ram_section_dirty_bitmap(container, section); } static void vfio_listener_log_sync(MemoryListener *listener, -- Gitee From f4523389bf57593484308124e06d67855bb79315 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Tue, 11 May 2021 10:08:14 +0800 Subject: [PATCH 118/486] vfio: Add vfio_prereg_listener_log_sync in nested stage In nested mode, we set up the stage 2 (gpa->hpa)and stage 1 (giova->gpa) separately by vfio_prereg_listener_region_add() and vfio_listener_region_add(). So when marking dirty pages we just need to pay attention to stage 2 mappings. Legacy vfio_listener_log_sync cannot be used in nested stage. This patch adds vfio_prereg_listener_log_sync to mark dirty pages in nested mode. Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 6136b1ef61..2506cd57ee 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1579,6 +1579,22 @@ static int vfio_dma_sync_ram_section_dirty_bitmap(VFIOContainer *container, int128_get64(section->size), ram_addr); } +static void vfio_prereg_listener_log_sync(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = + container_of(listener, VFIOContainer, prereg_listener); + + if (!memory_region_is_ram(section->mr) || + !container->dirty_pages_supported) { + return; + } + + if (vfio_devices_all_dirty_tracking(container)) { + vfio_dma_sync_ram_section_dirty_bitmap(container, section); + } +} + typedef struct { IOMMUNotifier n; VFIOGuestIOMMU *giommu; @@ -1666,6 +1682,16 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; + /* + * In nested mode, stage 2 (gpa->hpa) and stage 1 (giova->gpa) are + * set up separately. It is inappropriate to pass 'giova' to kernel + * to get dirty pages. We only need to focus on stage 2 mapping when + * marking dirty pages. + */ + if (container->iommu_type == VFIO_TYPE1_NESTING_IOMMU) { + return 0; + } + QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { if (MEMORY_REGION(giommu->iommu) == section->mr && giommu->n.start == section->offset_within_region) { @@ -1859,6 +1885,7 @@ static const MemoryListener vfio_memory_listener = { static MemoryListener vfio_memory_prereg_listener = { .region_add = vfio_prereg_listener_region_add, .region_del = vfio_prereg_listener_region_del, + .log_sync = vfio_prereg_listener_log_sync, }; static void vfio_listener_release(VFIOContainer *container) -- Gitee From 7086df6d90cd698a3e20cf4cf6e9a834f168cd8f Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Sat, 31 Jul 2021 09:40:24 +0800 Subject: [PATCH 119/486] vfio: Add vfio_prereg_listener_log_clear to re-enable mark dirty pages When tracking dirty pages, we just need to pay attention to stage 2 mappings. Legacy vfio_listener_log_clear cannot be used in nested stage. This patch adds vfio_prereg_listener_log_clear to re-enable dirty pages in nested mode. Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 2506cd57ee..20c820aa74 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1857,6 +1857,43 @@ static int vfio_physical_log_clear(VFIOContainer *container, return ret; } +static void vfio_prereg_listener_log_clear(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = + container_of(listener, VFIOContainer, prereg_listener); + + if (!memory_region_is_ram(section->mr)) { + return; + } + + vfio_physical_log_clear(container, section); +} + +static int vfio_clear_dirty_bitmap(VFIOContainer *container, + MemoryRegionSection *section) +{ + if (memory_region_is_iommu(section->mr)) { + /* + * In nested mode, stage 2 (gpa->hpa) and stage 1 (giova->gpa) are + * set up separately. It is inappropriate to pass 'giova' to kernel + * to get dirty pages. We only need to focus on stage 2 mapping when + * marking dirty pages. + */ + if (container->iommu_type == VFIO_TYPE1_NESTING_IOMMU) { + return 0; + } + + /* + * TODO: x86. With the log_clear() interface added, x86 may inplement + * its own method. + */ + } + + /* Here we assume that memory_region_is_ram(section->mr) == true */ + return vfio_physical_log_clear(container, section); +} + static void vfio_listener_log_clear(MemoryListener *listener, MemoryRegionSection *section) { @@ -1868,7 +1905,7 @@ static void vfio_listener_log_clear(MemoryListener *listener, } if (vfio_devices_all_dirty_tracking(container)) { - vfio_physical_log_clear(container, section); + vfio_clear_dirty_bitmap(container, section); } } @@ -1886,6 +1923,7 @@ static MemoryListener vfio_memory_prereg_listener = { .region_add = vfio_prereg_listener_region_add, .region_del = vfio_prereg_listener_region_del, .log_sync = vfio_prereg_listener_log_sync, + .log_clear = vfio_prereg_listener_log_clear, }; static void vfio_listener_release(VFIOContainer *container) -- Gitee From 287c63ab540533f1f9642e753c091caa7e6e2511 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Tue, 11 May 2021 10:08:15 +0800 Subject: [PATCH 120/486] vfio: Add vfio_prereg_listener_global_log_start/stop in nested stage In nested mode, we set up the stage 2 and stage 1 separately. In my opinion, vfio_memory_prereg_listener is used for stage 2 and vfio_memory_listener is used for stage 1. So it feels weird to call the global_log_start/stop interface in vfio_memory_listener to switch dirty tracking, although this won't cause any errors. Add global_log_start/stop interface in vfio_memory_prereg_listener can separate stage 2 from stage 1. Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 20c820aa74..65f3979492 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1501,6 +1501,17 @@ static void vfio_listener_log_global_start(MemoryListener *listener) { VFIOContainer *container = container_of(listener, VFIOContainer, listener); + /* For nested mode, vfio_prereg_listener is used to start dirty tracking */ + if (container->iommu_type != VFIO_TYPE1_NESTING_IOMMU) { + vfio_set_dirty_page_tracking(container, true); + } +} + +static void vfio_prereg_listener_log_global_start(MemoryListener *listener) +{ + VFIOContainer *container = + container_of(listener, VFIOContainer, prereg_listener); + vfio_set_dirty_page_tracking(container, true); } @@ -1508,6 +1519,17 @@ static void vfio_listener_log_global_stop(MemoryListener *listener) { VFIOContainer *container = container_of(listener, VFIOContainer, listener); + /* For nested mode, vfio_prereg_listener is used to stop dirty tracking */ + if (container->iommu_type != VFIO_TYPE1_NESTING_IOMMU) { + vfio_set_dirty_page_tracking(container, false); + } +} + +static void vfio_prereg_listener_log_global_stop(MemoryListener *listener) +{ + VFIOContainer *container = + container_of(listener, VFIOContainer, prereg_listener); + vfio_set_dirty_page_tracking(container, false); } @@ -1922,6 +1944,8 @@ static const MemoryListener vfio_memory_listener = { static MemoryListener vfio_memory_prereg_listener = { .region_add = vfio_prereg_listener_region_add, .region_del = vfio_prereg_listener_region_del, + .log_global_start = vfio_prereg_listener_log_global_start, + .log_global_stop = vfio_prereg_listener_log_global_stop, .log_sync = vfio_prereg_listener_log_sync, .log_clear = vfio_prereg_listener_log_clear, }; -- Gitee From 1b95c995f032c21bf6607dda8ede0f5856bb190a Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Tue, 11 May 2021 10:08:16 +0800 Subject: [PATCH 121/486] hw/arm/smmuv3: Post-load stage 1 configurations to the host In nested mode, we call the set_pasid_table() callback on each STE update to pass the guest stage 1 configuration to the host and apply it at physical level. In the case of live migration, we need to manually call the set_pasid_table() to load the guest stage 1 configurations to the host. If this operation fails, the migration fails. Signed-off-by: Kunkun Jiang --- hw/arm/smmuv3.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 45f21c53fe..291e3a12e8 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -932,7 +932,7 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) } } -static void smmuv3_notify_config_change(SMMUState *bs, uint32_t sid) +static int smmuv3_notify_config_change(SMMUState *bs, uint32_t sid) { #ifdef __linux__ IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); @@ -941,9 +941,10 @@ static void smmuv3_notify_config_change(SMMUState *bs, uint32_t sid) IOMMUConfig iommu_config = {}; SMMUTransCfg *cfg; SMMUDevice *sdev; + int ret; if (!mr) { - return; + return 0; } sdev = container_of(mr, SMMUDevice, iommu); @@ -952,13 +953,13 @@ static void smmuv3_notify_config_change(SMMUState *bs, uint32_t sid) smmuv3_flush_config(sdev); if (!pci_device_is_pasid_ops_set(sdev->bus, sdev->devfn)) { - return; + return 0; } cfg = smmuv3_get_config(sdev, &event); if (!cfg) { - return; + return 0; } iommu_config.pasid_cfg.argsz = sizeof(struct iommu_pasid_table_config); @@ -980,10 +981,13 @@ static void smmuv3_notify_config_change(SMMUState *bs, uint32_t sid) iommu_config.pasid_cfg.config, iommu_config.pasid_cfg.base_ptr); - if (pci_device_set_pasid_table(sdev->bus, sdev->devfn, &iommu_config)) { + ret = pci_device_set_pasid_table(sdev->bus, sdev->devfn, &iommu_config); + if (ret) { error_report("Failed to pass PASID table to host for iommu mr %s (%m)", mr->parent_obj.name); } + + return ret; #endif } @@ -1553,6 +1557,24 @@ static void smmu_realize(DeviceState *d, Error **errp) smmu_init_irq(s, dev); } +static int smmuv3_post_load(void *opaque, int version_id) +{ + SMMUv3State *s3 = opaque; + SMMUState *s = &(s3->smmu_state); + SMMUDevice *sdev; + int ret = 0; + + QLIST_FOREACH(sdev, &s->devices_with_notifiers, next) { + uint32_t sid = smmu_get_sid(sdev); + ret = smmuv3_notify_config_change(s, sid); + if (ret) { + break; + } + } + + return ret; +} + static const VMStateDescription vmstate_smmuv3_queue = { .name = "smmuv3_queue", .version_id = 1, @@ -1571,6 +1593,7 @@ static const VMStateDescription vmstate_smmuv3 = { .version_id = 1, .minimum_version_id = 1, .priority = MIG_PRI_IOMMU, + .post_load = smmuv3_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(features, SMMUv3State), VMSTATE_UINT8(sid_size, SMMUv3State), -- Gitee From c2a4ce033db6ab74256e28da382c797a98047d4b Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Tue, 7 Sep 2021 15:14:12 +0800 Subject: [PATCH 122/486] vfio/common: Fix incorrect address alignment in vfio_dma_map_ram_section The 'iova' will be passed to host kernel for mapping with the HPA. It is related to the host page size. So TARGET_PAGE_ALIGN should be replaced by REAL_HOST_PAGE_ALIGN. In the case of large granularity (64K), it may return early when map MMIO RAM section. And because of the inconsistency with vfio_dma_unmap_ram_section, it may cause 'assert(qrange)' in vfio_dma_unmap. Signed-off-by: Kunkun Jiang Signed-off-by: Zenghui Yu --- hw/vfio/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 65f3979492..89c49f5508 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1059,10 +1059,10 @@ static int vfio_dma_map_ram_section(VFIOContainer *container, assert(memory_region_is_ram(section->mr)); - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); end = int128_get64(int128_sub(llend, int128_one())); vaddr = memory_region_get_ram_ptr(section->mr) + -- Gitee From 00c553f53657bf4bc165d859187215dba7110246 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Tue, 14 Sep 2021 14:21:46 +0800 Subject: [PATCH 123/486] vfio/common: Add address alignment check in vfio_listener_region_del Both vfio_listener_region_add and vfio_listener_region_del have reference counting operations on ram section->mr. If the 'iova' and 'llend' of the ram section do not pass the alignment check, the ram section should not be mapped or unmapped. It means that the reference counting should not be changed. However, the address alignment check is missing in vfio_listener_region_del. This makes memory_region_unref will be unconditional called and causes unintended problems in some scenarios. Signed-off-by: Kunkun Jiang --- hw/vfio/common.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 89c49f5508..4d45c2b625 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1411,6 +1411,8 @@ static void vfio_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { VFIOContainer *container = container_of(listener, VFIOContainer, listener); + hwaddr iova; + Int128 llend; if (vfio_listener_skipped_section(section)) { trace_vfio_listener_region_del_skip( @@ -1460,6 +1462,14 @@ static void vfio_listener_region_del(MemoryListener *listener, */ } + iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); + llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); + if (int128_ge(int128_make64(iova), llend)) { + return; + } + vfio_dma_unmap_ram_section(container, section); memory_region_unref(section->mr); -- Gitee From bd8514594f0226b4599019ff123321138bb04d39 Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:25 +0800 Subject: [PATCH 124/486] target/arm: convert isar regs to array The isar in ARMCPU is a struct, each field of which represents an ID register. It's not convenient for us to support CPU feature in AArch64. So let's change it to an array first and add an enum as the index of the array for convenience. Since we will never access high 32-bits of ID registers in AArch32, it's harmless to change the ID registers in AArch32 to 64-bits. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- hw/intc/armv7m_nvic.c | 32 +-- target/arm/cpu.c | 105 ++++----- target/arm/cpu.h | 298 ++++++++++++------------ target/arm/cpu64.c | 234 +++++++++---------- target/arm/cpu_tcg.c | 503 +++++++++++++++++++++-------------------- target/arm/helper.c | 64 +++--- target/arm/hvf/hvf.c | 20 +- target/arm/internals.h | 14 +- target/arm/kvm64.c | 81 +++---- 9 files changed, 683 insertions(+), 668 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 13df002ce4..4b12b209b7 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1273,17 +1273,17 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_pfr0; + return cpu->isar.regs[ID_PFR0]; case 0xd44: /* PFR1. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_pfr1; + return cpu->isar.regs[ID_PFR1]; case 0xd48: /* DFR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_dfr0; + return cpu->isar.regs[ID_DFR0]; case 0xd4c: /* AFR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; @@ -1293,52 +1293,52 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr0; + return cpu->isar.regs[ID_MMFR0]; case 0xd54: /* MMFR1. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr1; + return cpu->isar.regs[ID_MMFR1]; case 0xd58: /* MMFR2. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr2; + return cpu->isar.regs[ID_MMFR2]; case 0xd5c: /* MMFR3. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr3; + return cpu->isar.regs[ID_MMFR3]; case 0xd60: /* ISAR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar0; + return cpu->isar.regs[ID_ISAR0]; case 0xd64: /* ISAR1. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar1; + return cpu->isar.regs[ID_ISAR1]; case 0xd68: /* ISAR2. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar2; + return cpu->isar.regs[ID_ISAR2]; case 0xd6c: /* ISAR3. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar3; + return cpu->isar.regs[ID_ISAR3]; case 0xd70: /* ISAR4. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar4; + return cpu->isar.regs[ID_ISAR4]; case 0xd74: /* ISAR5. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar5; + return cpu->isar.regs[ID_ISAR5]; case 0xd78: /* CLIDR */ return cpu->clidr; case 0xd7c: /* CTR */ @@ -1548,11 +1548,11 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) } return cpu->env.v7m.fpdscr[attrs.secure]; case 0xf40: /* MVFR0 */ - return cpu->isar.mvfr0; + return cpu->isar.regs[MVFR0]; case 0xf44: /* MVFR1 */ - return cpu->isar.mvfr1; + return cpu->isar.regs[MVFR1]; case 0xf48: /* MVFR2 */ - return cpu->isar.mvfr2; + return cpu->isar.regs[MVFR2]; default: bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a211804fd3..f1ce0474a3 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -176,9 +176,9 @@ static void arm_cpu_reset(DeviceState *dev) g_hash_table_foreach(cpu->cp_regs, cp_reg_check_reset, cpu); env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; - env->vfp.xregs[ARM_VFP_MVFR0] = cpu->isar.mvfr0; - env->vfp.xregs[ARM_VFP_MVFR1] = cpu->isar.mvfr1; - env->vfp.xregs[ARM_VFP_MVFR2] = cpu->isar.mvfr2; + env->vfp.xregs[ARM_VFP_MVFR0] = cpu->isar.regs[MVFR0]; + env->vfp.xregs[ARM_VFP_MVFR1] = cpu->isar.regs[MVFR1]; + env->vfp.xregs[ARM_VFP_MVFR2] = cpu->isar.regs[MVFR2]; cpu->power_state = s->start_powered_off ? PSCI_OFF : PSCI_ON; @@ -1520,20 +1520,20 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) uint64_t t; uint32_t u; - t = cpu->isar.id_aa64isar1; + t = cpu->isar.regs[ID_AA64ISAR1]; t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 0); - cpu->isar.id_aa64isar1 = t; + cpu->isar.regs[ID_AA64ISAR1] = t; - t = cpu->isar.id_aa64pfr0; + t = cpu->isar.regs[ID_AA64PFR0]; t = FIELD_DP64(t, ID_AA64PFR0, FP, 0xf); - cpu->isar.id_aa64pfr0 = t; + cpu->isar.regs[ID_AA64PFR0] = t; - u = cpu->isar.id_isar6; + u = cpu->isar.regs[ID_ISAR6]; u = FIELD_DP32(u, ID_ISAR6, JSCVT, 0); u = FIELD_DP32(u, ID_ISAR6, BF16, 0); - cpu->isar.id_isar6 = u; + cpu->isar.regs[ID_ISAR6] = u; - u = cpu->isar.mvfr0; + u = cpu->isar.regs[MVFR0]; u = FIELD_DP32(u, MVFR0, FPSP, 0); u = FIELD_DP32(u, MVFR0, FPDP, 0); u = FIELD_DP32(u, MVFR0, FPDIVIDE, 0); @@ -1543,20 +1543,20 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) u = FIELD_DP32(u, MVFR0, FPTRAP, 0); u = FIELD_DP32(u, MVFR0, FPSHVEC, 0); } - cpu->isar.mvfr0 = u; + cpu->isar.regs[MVFR0] = u; - u = cpu->isar.mvfr1; + u = cpu->isar.regs[MVFR1]; u = FIELD_DP32(u, MVFR1, FPFTZ, 0); u = FIELD_DP32(u, MVFR1, FPDNAN, 0); u = FIELD_DP32(u, MVFR1, FPHP, 0); if (arm_feature(env, ARM_FEATURE_M)) { u = FIELD_DP32(u, MVFR1, FP16, 0); } - cpu->isar.mvfr1 = u; + cpu->isar.regs[MVFR1] = u; - u = cpu->isar.mvfr2; + u = cpu->isar.regs[MVFR2]; u = FIELD_DP32(u, MVFR2, FPMISC, 0); - cpu->isar.mvfr2 = u; + cpu->isar.regs[MVFR2] = u; } if (!cpu->has_neon) { @@ -1565,43 +1565,43 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) unset_feature(env, ARM_FEATURE_NEON); - t = cpu->isar.id_aa64isar0; + t = cpu->isar.regs[ID_AA64ISAR0]; t = FIELD_DP64(t, ID_AA64ISAR0, DP, 0); - cpu->isar.id_aa64isar0 = t; + cpu->isar.regs[ID_AA64ISAR0] = t; - t = cpu->isar.id_aa64isar1; + t = cpu->isar.regs[ID_AA64ISAR1]; t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 0); t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 0); t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 0); - cpu->isar.id_aa64isar1 = t; + cpu->isar.regs[ID_AA64ISAR1] = t; - t = cpu->isar.id_aa64pfr0; + t = cpu->isar.regs[ID_AA64PFR0]; t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 0xf); - cpu->isar.id_aa64pfr0 = t; + cpu->isar.regs[ID_AA64PFR0] = t; - u = cpu->isar.id_isar5; + u = cpu->isar.regs[ID_ISAR5]; u = FIELD_DP32(u, ID_ISAR5, RDM, 0); u = FIELD_DP32(u, ID_ISAR5, VCMA, 0); - cpu->isar.id_isar5 = u; + cpu->isar.regs[ID_ISAR5] = u; - u = cpu->isar.id_isar6; + u = cpu->isar.regs[ID_ISAR6]; u = FIELD_DP32(u, ID_ISAR6, DP, 0); u = FIELD_DP32(u, ID_ISAR6, FHM, 0); u = FIELD_DP32(u, ID_ISAR6, BF16, 0); u = FIELD_DP32(u, ID_ISAR6, I8MM, 0); - cpu->isar.id_isar6 = u; + cpu->isar.regs[ID_ISAR6] = u; if (!arm_feature(env, ARM_FEATURE_M)) { - u = cpu->isar.mvfr1; + u = cpu->isar.regs[MVFR1]; u = FIELD_DP32(u, MVFR1, SIMDLS, 0); u = FIELD_DP32(u, MVFR1, SIMDINT, 0); u = FIELD_DP32(u, MVFR1, SIMDSP, 0); u = FIELD_DP32(u, MVFR1, SIMDHP, 0); - cpu->isar.mvfr1 = u; + cpu->isar.regs[MVFR1] = u; - u = cpu->isar.mvfr2; + u = cpu->isar.regs[MVFR2]; u = FIELD_DP32(u, MVFR2, SIMDMISC, 0); - cpu->isar.mvfr2 = u; + cpu->isar.regs[MVFR2] = u; } } @@ -1609,22 +1609,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) uint64_t t; uint32_t u; - t = cpu->isar.id_aa64isar0; + t = cpu->isar.regs[ID_AA64ISAR0]; t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 0); - cpu->isar.id_aa64isar0 = t; + cpu->isar.regs[ID_AA64ISAR0] = t; - t = cpu->isar.id_aa64isar1; + t = cpu->isar.regs[ID_AA64ISAR1]; t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 0); - cpu->isar.id_aa64isar1 = t; + cpu->isar.regs[ID_AA64ISAR1] = t; - u = cpu->isar.mvfr0; + u = cpu->isar.regs[MVFR0]; u = FIELD_DP32(u, MVFR0, SIMDREG, 0); - cpu->isar.mvfr0 = u; + cpu->isar.regs[MVFR0] = u; /* Despite the name, this field covers both VFP and Neon */ - u = cpu->isar.mvfr1; + u = cpu->isar.regs[MVFR1]; u = FIELD_DP32(u, MVFR1, SIMDFMAC, 0); - cpu->isar.mvfr1 = u; + cpu->isar.regs[MVFR1] = u; } if (arm_feature(env, ARM_FEATURE_M) && !cpu->has_dsp) { @@ -1632,19 +1632,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) unset_feature(env, ARM_FEATURE_THUMB_DSP); - u = cpu->isar.id_isar1; + u = cpu->isar.regs[ID_ISAR1]; u = FIELD_DP32(u, ID_ISAR1, EXTEND, 1); - cpu->isar.id_isar1 = u; + cpu->isar.regs[ID_ISAR1] = u; - u = cpu->isar.id_isar2; + u = cpu->isar.regs[ID_ISAR2]; u = FIELD_DP32(u, ID_ISAR2, MULTU, 1); u = FIELD_DP32(u, ID_ISAR2, MULTS, 1); - cpu->isar.id_isar2 = u; + cpu->isar.regs[ID_ISAR2] = u; - u = cpu->isar.id_isar3; + u = cpu->isar.regs[ID_ISAR3]; u = FIELD_DP32(u, ID_ISAR3, SIMD, 1); u = FIELD_DP32(u, ID_ISAR3, SATURATE, 0); - cpu->isar.id_isar3 = u; + cpu->isar.regs[ID_ISAR3] = u; } /* Some features automatically imply others: */ @@ -1785,8 +1785,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* Disable the security extension feature bits in the processor feature * registers as well. These are id_pfr1[7:4] and id_aa64pfr0[15:12]. */ - cpu->isar.id_pfr1 &= ~0xf0; - cpu->isar.id_aa64pfr0 &= ~0xf000; + cpu->isar.regs[ID_PFR1] &= ~0xf0; + cpu->isar.regs[ID_AA64PFR0] &= ~0xf000; } if (!cpu->has_el2) { @@ -1809,9 +1809,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu); #endif } else { - cpu->isar.id_aa64dfr0 = - FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, PMUVER, 0); - cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, PERFMON, 0); + cpu->isar.regs[ID_AA64DFR0] = + FIELD_DP64(cpu->isar.regs[ID_AA64DFR0], ID_AA64DFR0, PMUVER, 0); + cpu->isar.regs[ID_DFR0] = FIELD_DP32(cpu->isar.regs[ID_DFR0], ID_DFR0, + PERFMON, 0); cpu->pmceid0 = 0; cpu->pmceid1 = 0; } @@ -1821,8 +1822,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * registers if we don't have EL2. These are id_pfr1[15:12] and * id_aa64pfr0_el1[11:8]. */ - cpu->isar.id_aa64pfr0 &= ~0xf00; - cpu->isar.id_pfr1 &= ~0xf000; + cpu->isar.regs[ID_AA64PFR0] &= ~0xf00; + cpu->isar.regs[ID_PFR1] &= ~0xf000; } #ifndef CONFIG_USER_ONLY @@ -1831,8 +1832,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * Disable the MTE feature bits if we do not have tag-memory * provided by the machine. */ - cpu->isar.id_aa64pfr1 = - FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0); + cpu->isar.regs[ID_AA64PFR1] = + FIELD_DP64(cpu->isar.regs[ID_AA64PFR1], ID_AA64PFR1, MTE, 0); } #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index e33f37b70a..3dda33f347 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -69,6 +69,41 @@ #define ARMV7M_EXCP_PENDSV 14 #define ARMV7M_EXCP_SYSTICK 15 +typedef enum CPUIDReg { + MIDR_EL1, + ID_ISAR0, + ID_ISAR1, + ID_ISAR2, + ID_ISAR3, + ID_ISAR4, + ID_ISAR5, + ID_ISAR6, + ID_PFR0, + ID_PFR1, + ID_PFR2, + ID_MMFR0, + ID_MMFR1, + ID_MMFR2, + ID_MMFR3, + ID_MMFR4, + ID_AA64ISAR0, + ID_AA64ISAR1, + ID_AA64PFR0, + ID_AA64PFR1, + ID_AA64MMFR0, + ID_AA64MMFR1, + ID_AA64MMFR2, + ID_AA64DFR0, + ID_AA64DFR1, + ID_AA64ZFR0, + ID_DFR0, + MVFR0, + MVFR1, + MVFR2, + DBGDIDR, + ID_MAX, +} CPUIDReg; + /* For M profile, some registers are banked secure vs non-secure; * these are represented as a 2-element array where the first element * is the non-secure copy and the second is the secure copy. @@ -922,36 +957,7 @@ struct ARMCPU { * field by reading the value from the KVM vCPU. */ struct ARMISARegisters { - uint32_t id_isar0; - uint32_t id_isar1; - uint32_t id_isar2; - uint32_t id_isar3; - uint32_t id_isar4; - uint32_t id_isar5; - uint32_t id_isar6; - uint32_t id_mmfr0; - uint32_t id_mmfr1; - uint32_t id_mmfr2; - uint32_t id_mmfr3; - uint32_t id_mmfr4; - uint32_t id_pfr0; - uint32_t id_pfr1; - uint32_t id_pfr2; - uint32_t mvfr0; - uint32_t mvfr1; - uint32_t mvfr2; - uint32_t id_dfr0; - uint32_t dbgdidr; - uint64_t id_aa64isar0; - uint64_t id_aa64isar1; - uint64_t id_aa64pfr0; - uint64_t id_aa64pfr1; - uint64_t id_aa64mmfr0; - uint64_t id_aa64mmfr1; - uint64_t id_aa64mmfr2; - uint64_t id_aa64dfr0; - uint64_t id_aa64dfr1; - uint64_t id_aa64zfr0; + uint64_t regs[ID_MAX]; } isar; uint64_t midr; uint32_t revidr; @@ -3729,103 +3735,103 @@ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) */ static inline bool isar_feature_aa32_thumb_div(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; + return FIELD_EX32(id->regs[ID_ISAR0], ID_ISAR0, DIVIDE) != 0; } static inline bool isar_feature_aa32_arm_div(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) > 1; + return FIELD_EX32(id->regs[ID_ISAR0], ID_ISAR0, DIVIDE) > 1; } static inline bool isar_feature_aa32_lob(const ARMISARegisters *id) { /* (M-profile) low-overhead loops and branch future */ - return FIELD_EX32(id->id_isar0, ID_ISAR0, CMPBRANCH) >= 3; + return FIELD_EX32(id->regs[ID_ISAR0], ID_ISAR0, CMPBRANCH) >= 3; } static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; + return FIELD_EX32(id->regs[ID_ISAR1], ID_ISAR1, JAZELLE) != 0; } static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, AES) != 0; } static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, AES) > 1; } static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, SHA1) != 0; } static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, SHA2) != 0; } static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, CRC32) != 0; } static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, RDM) != 0; } static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; + return FIELD_EX32(id->regs[ID_ISAR5], ID_ISAR5, VCMA) != 0; } static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, JSCVT) != 0; } static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, DP) != 0; } static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, FHM) != 0; } static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, SB) != 0; } static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, SPECRES) != 0; } static inline bool isar_feature_aa32_bf16(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, BF16) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, BF16) != 0; } static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, I8MM) != 0; + return FIELD_EX32(id->regs[ID_ISAR6], ID_ISAR6, I8MM) != 0; } static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0; + return FIELD_EX32(id->regs[ID_PFR0], ID_PFR0, RAS) != 0; } static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0; + return FIELD_EX32(id->regs[ID_PFR1], ID_PFR1, MPROGMOD) != 0; } static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) @@ -3834,16 +3840,16 @@ static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) * Return true if M-profile state handling insns * (VSCCLRM, CLRM, FPCTX access insns) are implemented */ - return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3; + return FIELD_EX32(id->regs[ID_PFR1], ID_PFR1, SECURITY) >= 3; } static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) { /* Sadly this is encoded differently for A-profile and M-profile */ if (isar_feature_aa32_mprofile(id)) { - return FIELD_EX32(id->mvfr1, MVFR1, FP16) > 0; + return FIELD_EX32(id->regs[MVFR1], MVFR1, FP16) > 0; } else { - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) >= 3; + return FIELD_EX32(id->regs[MVFR1], MVFR1, FPHP) >= 3; } } @@ -3855,7 +3861,7 @@ static inline bool isar_feature_aa32_mve(const ARMISARegisters *id) * else for A-profile. */ return isar_feature_aa32_mprofile(id) && - FIELD_EX32(id->mvfr1, MVFR1, MVE) > 0; + FIELD_EX32(id->regs[MVFR1], MVFR1, MVE) > 0; } static inline bool isar_feature_aa32_mve_fp(const ARMISARegisters *id) @@ -3866,7 +3872,7 @@ static inline bool isar_feature_aa32_mve_fp(const ARMISARegisters *id) * else for A-profile. */ return isar_feature_aa32_mprofile(id) && - FIELD_EX32(id->mvfr1, MVFR1, MVE) >= 2; + FIELD_EX32(id->regs[MVFR1], MVFR1, MVE) >= 2; } static inline bool isar_feature_aa32_vfp_simd(const ARMISARegisters *id) @@ -3875,42 +3881,42 @@ static inline bool isar_feature_aa32_vfp_simd(const ARMISARegisters *id) * Return true if either VFP or SIMD is implemented. * In this case, a minimum of VFP w/ D0-D15. */ - return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) > 0; + return FIELD_EX32(id->regs[MVFR0], MVFR0, SIMDREG) > 0; } static inline bool isar_feature_aa32_simd_r32(const ARMISARegisters *id) { /* Return true if D16-D31 are implemented */ - return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) >= 2; + return FIELD_EX32(id->regs[MVFR0], MVFR0, SIMDREG) >= 2; } static inline bool isar_feature_aa32_fpshvec(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr0, MVFR0, FPSHVEC) > 0; + return FIELD_EX32(id->regs[MVFR0], MVFR0, FPSHVEC) > 0; } static inline bool isar_feature_aa32_fpsp_v2(const ARMISARegisters *id) { /* Return true if CPU supports single precision floating point, VFPv2 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPSP) > 0; + return FIELD_EX32(id->regs[MVFR0], MVFR0, FPSP) > 0; } static inline bool isar_feature_aa32_fpsp_v3(const ARMISARegisters *id) { /* Return true if CPU supports single precision floating point, VFPv3 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPSP) >= 2; + return FIELD_EX32(id->regs[MVFR0], MVFR0, FPSP) >= 2; } static inline bool isar_feature_aa32_fpdp_v2(const ARMISARegisters *id) { /* Return true if CPU supports double precision floating point, VFPv2 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPDP) > 0; + return FIELD_EX32(id->regs[MVFR0], MVFR0, FPDP) > 0; } static inline bool isar_feature_aa32_fpdp_v3(const ARMISARegisters *id) { /* Return true if CPU supports double precision floating point, VFPv3 */ - return FIELD_EX32(id->mvfr0, MVFR0, FPDP) >= 2; + return FIELD_EX32(id->regs[MVFR0], MVFR0, FPDP) >= 2; } static inline bool isar_feature_aa32_vfp(const ARMISARegisters *id) @@ -3925,12 +3931,12 @@ static inline bool isar_feature_aa32_vfp(const ARMISARegisters *id) */ static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 0; + return FIELD_EX32(id->regs[MVFR1], MVFR1, FPHP) > 0; } static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 1; + return FIELD_EX32(id->regs[MVFR1], MVFR1, FPHP) > 1; } /* @@ -3942,86 +3948,86 @@ static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) */ static inline bool isar_feature_aa32_simdfmac(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr1, MVFR1, SIMDFMAC) != 0; + return FIELD_EX32(id->regs[MVFR1], MVFR1, SIMDFMAC) != 0; } static inline bool isar_feature_aa32_vsel(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 1; + return FIELD_EX32(id->regs[MVFR2], MVFR2, FPMISC) >= 1; } static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 2; + return FIELD_EX32(id->regs[MVFR2], MVFR2, FPMISC) >= 2; } static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 3; + return FIELD_EX32(id->regs[MVFR2], MVFR2, FPMISC) >= 3; } static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) { - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 4; + return FIELD_EX32(id->regs[MVFR2], MVFR2, FPMISC) >= 4; } static inline bool isar_feature_aa32_pxn(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr0, ID_MMFR0, VMSA) >= 4; + return FIELD_EX32(id->regs[ID_MMFR0], ID_MMFR0, VMSA) >= 4; } static inline bool isar_feature_aa32_pan(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) != 0; + return FIELD_EX32(id->regs[ID_MMFR3], ID_MMFR3, PAN) != 0; } static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2; + return FIELD_EX32(id->regs[ID_MMFR3], ID_MMFR3, PAN) >= 2; } static inline bool isar_feature_aa32_pmu_8_1(const ARMISARegisters *id) { /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 4 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; + return FIELD_EX32(id->regs[ID_DFR0], ID_DFR0, PERFMON) >= 4 && + FIELD_EX32(id->regs[ID_DFR0], ID_DFR0, PERFMON) != 0xf; } static inline bool isar_feature_aa32_pmu_8_4(const ARMISARegisters *id) { /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 5 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; + return FIELD_EX32(id->regs[ID_DFR0], ID_DFR0, PERFMON) >= 5 && + FIELD_EX32(id->regs[ID_DFR0], ID_DFR0, PERFMON) != 0xf; } static inline bool isar_feature_aa32_hpd(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, HPDS) != 0; + return FIELD_EX32(id->regs[ID_MMFR4], ID_MMFR4, HPDS) != 0; } static inline bool isar_feature_aa32_ac2(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, AC2) != 0; + return FIELD_EX32(id->regs[ID_MMFR4], ID_MMFR4, AC2) != 0; } static inline bool isar_feature_aa32_ccidx(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, CCIDX) != 0; + return FIELD_EX32(id->regs[ID_MMFR4], ID_MMFR4, CCIDX) != 0; } static inline bool isar_feature_aa32_tts2uxn(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, XNX) != 0; + return FIELD_EX32(id->regs[ID_MMFR4], ID_MMFR4, XNX) != 0; } static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0; + return FIELD_EX32(id->regs[ID_PFR0], ID_PFR0, DIT) != 0; } static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; + return FIELD_EX32(id->regs[ID_PFR2], ID_PFR2, SSBS) != 0; } /* @@ -4029,92 +4035,92 @@ static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) */ static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, AES) != 0; } static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, AES) > 1; } static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, SHA1) != 0; } static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, SHA2) != 0; } static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, SHA2) > 1; } static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, CRC32) != 0; } static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, ATOMIC) != 0; } static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, RDM) != 0; } static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, SHA3) != 0; } static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, SM3) != 0; } static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, SM4) != 0; } static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, DP) != 0; } static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, FHM) != 0; } static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, TS) != 0; } static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, TS) >= 2; } static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, RNDR) != 0; } static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, JSCVT) != 0; } static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, FCMA) != 0; } static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) @@ -4123,7 +4129,7 @@ static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) * Return true if any form of pauth is enabled, as this * predicate controls migration of the 128-bit keys. */ - return (id->id_aa64isar1 & + return (id->regs[ID_AA64ISAR1] & (FIELD_DP64(0, ID_AA64ISAR1, APA, 0xf) | FIELD_DP64(0, ID_AA64ISAR1, API, 0xf) | FIELD_DP64(0, ID_AA64ISAR1, GPA, 0xf) | @@ -4136,221 +4142,221 @@ static inline bool isar_feature_aa64_pauth_arch(const ARMISARegisters *id) * Return true if pauth is enabled with the architected QARMA algorithm. * QEMU will always set APA+GPA to the same value. */ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, APA) != 0; } static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, TLB) == 2; } static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR0], ID_AA64ISAR0, TLB) != 0; } static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, SB) != 0; } static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, SPECRES) != 0; } static inline bool isar_feature_aa64_frint(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, FRINTTS) != 0; } static inline bool isar_feature_aa64_dcpop(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, DPB) != 0; } static inline bool isar_feature_aa64_dcpodp(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) >= 2; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, DPB) >= 2; } static inline bool isar_feature_aa64_bf16(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, BF16) != 0; } static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) != 0xf; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, FP) != 0xf; } static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically wrt FP16. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, FP) == 1; } static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, EL0) >= 2; } static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, EL1) >= 2; } static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, SVE) != 0; } static inline bool isar_feature_aa64_sel2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 0; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, SEL2) != 0; } static inline bool isar_feature_aa64_vh(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR1], ID_AA64MMFR1, VH) != 0; } static inline bool isar_feature_aa64_lor(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, LO) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR1], ID_AA64MMFR1, LO) != 0; } static inline bool isar_feature_aa64_pan(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR1], ID_AA64MMFR1, PAN) != 0; } static inline bool isar_feature_aa64_ats1e1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 2; + return FIELD_EX64(id->regs[ID_AA64MMFR1], ID_AA64MMFR1, PAN) >= 2; } static inline bool isar_feature_aa64_uao(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, UAO) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR2], ID_AA64MMFR2, UAO) != 0; } static inline bool isar_feature_aa64_st(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR2], ID_AA64MMFR2, ST) != 0; } static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; + return FIELD_EX64(id->regs[ID_AA64PFR1], ID_AA64PFR1, BT) != 0; } static inline bool isar_feature_aa64_mte_insn_reg(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; + return FIELD_EX64(id->regs[ID_AA64PFR1], ID_AA64PFR1, MTE) != 0; } static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; + return FIELD_EX64(id->regs[ID_AA64PFR1], ID_AA64PFR1, MTE) >= 2; } static inline bool isar_feature_aa64_pmu_8_1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; + return FIELD_EX64(id->regs[ID_AA64DFR0], ID_AA64DFR0, PMUVER) >= 4 && + FIELD_EX64(id->regs[ID_AA64DFR0], ID_AA64DFR0, PMUVER) != 0xf; } static inline bool isar_feature_aa64_pmu_8_4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 5 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; + return FIELD_EX64(id->regs[ID_AA64DFR0], ID_AA64DFR0, PMUVER) >= 5 && + FIELD_EX64(id->regs[ID_AA64DFR0], ID_AA64DFR0, PMUVER) != 0xf; } static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, LRCPC) != 0; } static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, LRCPC) >= 2; } static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; + return FIELD_EX64(id->regs[ID_AA64ISAR1], ID_AA64ISAR1, I8MM) != 0; } static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR2], ID_AA64MMFR2, CCIDX) != 0; } static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; + return FIELD_EX64(id->regs[ID_AA64MMFR1], ID_AA64MMFR1, XNX) != 0; } static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; + return FIELD_EX64(id->regs[ID_AA64PFR0], ID_AA64PFR0, DIT) != 0; } static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; + return FIELD_EX64(id->regs[ID_AA64PFR1], ID_AA64PFR1, SSBS) != 0; } static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, SVEVER) != 0; } static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, AES) != 0; } static inline bool isar_feature_aa64_sve2_pmull128(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) >= 2; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, AES) >= 2; } static inline bool isar_feature_aa64_sve2_bitperm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BITPERM) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, BITPERM) != 0; } static inline bool isar_feature_aa64_sve_bf16(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BFLOAT16) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, BFLOAT16) != 0; } static inline bool isar_feature_aa64_sve2_sha3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SHA3) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, SHA3) != 0; } static inline bool isar_feature_aa64_sve2_sm4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SM4) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, SM4) != 0; } static inline bool isar_feature_aa64_sve_i8mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, I8MM) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, I8MM) != 0; } static inline bool isar_feature_aa64_sve_f32mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F32MM) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, F32MM) != 0; } static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F64MM) != 0; + return FIELD_EX64(id->regs[ID_AA64ZFR0], ID_AA64ZFR0, F64MM) != 0; } /* diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1b56261964..96a49a3158 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -108,31 +108,31 @@ static void aarch64_a57_initfn(Object *obj) cpu->midr = 0x411fd070; cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034070; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x12111111; - cpu->isar.mvfr2 = 0x00000043; + cpu->isar.regs[MVFR0] = 0x10110222; + cpu->isar.regs[MVFR1] = 0x12111111; + cpu->isar.regs[MVFR2] = 0x00000043; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x03010066; + cpu->isar.regs[ID_PFR0] = 0x00000131; + cpu->isar.regs[ID_PFR1] = 0x00011011; + cpu->isar.regs[ID_DFR0] = 0x03010066; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; - cpu->isar.id_aa64mmfr0 = 0x00001124; - cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.regs[ID_MMFR0] = 0x10101105; + cpu->isar.regs[ID_MMFR1] = 0x40000000; + cpu->isar.regs[ID_MMFR2] = 0x01260000; + cpu->isar.regs[ID_MMFR3] = 0x02102211; + cpu->isar.regs[ID_ISAR0] = 0x02101110; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232042; + cpu->isar.regs[ID_ISAR3] = 0x01112131; + cpu->isar.regs[ID_ISAR4] = 0x00011142; + cpu->isar.regs[ID_ISAR5] = 0x00011121; + cpu->isar.regs[ID_ISAR6] = 0; + cpu->isar.regs[ID_AA64PFR0] = 0x00002222; + cpu->isar.regs[ID_AA64DFR0] = 0x10305106; + cpu->isar.regs[ID_AA64ISAR0] = 0x00011120; + cpu->isar.regs[ID_AA64MMFR0] = 0x00001124; + cpu->isar.regs[DBGDIDR] = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ @@ -161,31 +161,31 @@ static void aarch64_a53_initfn(Object *obj) cpu->midr = 0x410fd034; cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034070; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x12111111; - cpu->isar.mvfr2 = 0x00000043; + cpu->isar.regs[MVFR0] = 0x10110222; + cpu->isar.regs[MVFR1] = 0x12111111; + cpu->isar.regs[MVFR2] = 0x00000043; cpu->ctr = 0x84448004; /* L1Ip = VIPT */ cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x03010066; + cpu->isar.regs[ID_PFR0] = 0x00000131; + cpu->isar.regs[ID_PFR1] = 0x00011011; + cpu->isar.regs[ID_DFR0] = 0x03010066; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; - cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ - cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.regs[ID_MMFR0] = 0x10101105; + cpu->isar.regs[ID_MMFR1] = 0x40000000; + cpu->isar.regs[ID_MMFR2] = 0x01260000; + cpu->isar.regs[ID_MMFR3] = 0x02102211; + cpu->isar.regs[ID_ISAR0] = 0x02101110; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232042; + cpu->isar.regs[ID_ISAR3] = 0x01112131; + cpu->isar.regs[ID_ISAR4] = 0x00011142; + cpu->isar.regs[ID_ISAR5] = 0x00011121; + cpu->isar.regs[ID_ISAR6] = 0; + cpu->isar.regs[ID_AA64PFR0] = 0x00002222; + cpu->isar.regs[ID_AA64DFR0] = 0x10305106; + cpu->isar.regs[ID_AA64ISAR0] = 0x00011120; + cpu->isar.regs[ID_AA64MMFR0] = 0x00001122; /* 40 bit physical addr */ + cpu->isar.regs[DBGDIDR] = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ @@ -214,30 +214,30 @@ static void aarch64_a72_initfn(Object *obj) cpu->midr = 0x410fd083; cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034080; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x12111111; - cpu->isar.mvfr2 = 0x00000043; + cpu->isar.regs[MVFR0] = 0x10110222; + cpu->isar.regs[MVFR1] = 0x12111111; + cpu->isar.regs[MVFR2] = 0x00000043; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x03010066; + cpu->isar.regs[ID_PFR0] = 0x00000131; + cpu->isar.regs[ID_PFR1] = 0x00011011; + cpu->isar.regs[ID_DFR0] = 0x03010066; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; - cpu->isar.id_aa64mmfr0 = 0x00001124; - cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.regs[ID_MMFR0] = 0x10201105; + cpu->isar.regs[ID_MMFR1] = 0x40000000; + cpu->isar.regs[ID_MMFR2] = 0x01260000; + cpu->isar.regs[ID_MMFR3] = 0x02102211; + cpu->isar.regs[ID_ISAR0] = 0x02101110; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232042; + cpu->isar.regs[ID_ISAR3] = 0x01112131; + cpu->isar.regs[ID_ISAR4] = 0x00011142; + cpu->isar.regs[ID_ISAR5] = 0x00011121; + cpu->isar.regs[ID_AA64PFR0] = 0x00002222; + cpu->isar.regs[ID_AA64DFR0] = 0x10305106; + cpu->isar.regs[ID_AA64ISAR0] = 0x00011120; + cpu->isar.regs[ID_AA64MMFR0] = 0x00001124; + cpu->isar.regs[DBGDIDR] = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ @@ -262,10 +262,10 @@ static void aarch64_kunpeng_920_initfn(Object *obj) cpu->midr = 0x480fd010; cpu->ctr = 0x84448004; - cpu->isar.id_aa64pfr0 = 0x11001111; - cpu->isar.id_aa64dfr0 = 0x110305408; - cpu->isar.id_aa64isar0 = 0x10211120; - cpu->isar.id_aa64mmfr0 = 0x101125; + cpu->isar.regs[ID_AA64PFR0] = 0x11001111; + cpu->isar.regs[ID_AA64DFR0] = 0x110305408; + cpu->isar.regs[ID_AA64ISAR0] = 0x10211120; + cpu->isar.regs[ID_AA64MMFR0] = 0x101125; } void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) @@ -566,9 +566,9 @@ static void cpu_arm_set_sve(Object *obj, bool value, Error **errp) return; } - t = cpu->isar.id_aa64pfr0; + t = cpu->isar.regs[ID_AA64PFR0]; t = FIELD_DP64(t, ID_AA64PFR0, SVE, value); - cpu->isar.id_aa64pfr0 = t; + cpu->isar.regs[ID_AA64PFR0] = t; } #ifdef CONFIG_USER_ONLY @@ -662,12 +662,12 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) error_append_hint(errp, "Add pauth=on to the CPU property list.\n"); } - t = cpu->isar.id_aa64isar1; + t = cpu->isar.regs[ID_AA64ISAR1]; t = FIELD_DP64(t, ID_AA64ISAR1, APA, arch_val); t = FIELD_DP64(t, ID_AA64ISAR1, GPA, arch_val); t = FIELD_DP64(t, ID_AA64ISAR1, API, impdef_val); t = FIELD_DP64(t, ID_AA64ISAR1, GPI, impdef_val); - cpu->isar.id_aa64isar1 = t; + cpu->isar.regs[ID_AA64ISAR1] = t; } static Property arm_cpu_pauth_property = @@ -736,7 +736,7 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, MIDR_EL1, REVISION, 0); cpu->midr = t; - t = cpu->isar.id_aa64isar0; + t = cpu->isar.regs[ID_AA64ISAR0]; t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* AES + PMULL */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* SHA512 */ @@ -751,9 +751,9 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* v8.5-CondM */ t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* FEAT_TLBIRANGE */ t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); - cpu->isar.id_aa64isar0 = t; + cpu->isar.regs[ID_AA64ISAR0] = t; - t = cpu->isar.id_aa64isar1; + t = cpu->isar.regs[ID_AA64ISAR1]; t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); @@ -763,17 +763,17 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* ARMv8.4-RCPC */ t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); - cpu->isar.id_aa64isar1 = t; + cpu->isar.regs[ID_AA64ISAR1] = t; - t = cpu->isar.id_aa64pfr0; + t = cpu->isar.regs[ID_AA64PFR0]; t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); - cpu->isar.id_aa64pfr0 = t; + cpu->isar.regs[ID_AA64PFR0] = t; - t = cpu->isar.id_aa64pfr1; + t = cpu->isar.regs[ID_AA64PFR1]; t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* @@ -782,28 +782,28 @@ static void aarch64_max_initfn(Object *obj) * we do for EL2 with the virtualization=on property. */ t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); - cpu->isar.id_aa64pfr1 = t; + cpu->isar.regs[ID_AA64PFR1] = t; - t = cpu->isar.id_aa64mmfr0; + t = cpu->isar.regs[ID_AA64MMFR0]; t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 5); /* PARange: 48 bits */ - cpu->isar.id_aa64mmfr0 = t; + cpu->isar.regs[ID_AA64MMFR0] = t; - t = cpu->isar.id_aa64mmfr1; + t = cpu->isar.regs[ID_AA64MMFR1]; t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 1); /* HPD */ t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 2); /* ATS1E1 */ t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* VMID16 */ t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* TTS2UXN */ - cpu->isar.id_aa64mmfr1 = t; + cpu->isar.regs[ID_AA64MMFR1] = t; - t = cpu->isar.id_aa64mmfr2; + t = cpu->isar.regs[ID_AA64MMFR2]; t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* TTCNP */ t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* TTST */ - cpu->isar.id_aa64mmfr2 = t; + cpu->isar.regs[ID_AA64MMFR2] = t; - t = cpu->isar.id_aa64zfr0; + t = cpu->isar.regs[ID_AA64ZFR0]; t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* PMULL */ t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); @@ -813,19 +813,19 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); - cpu->isar.id_aa64zfr0 = t; + cpu->isar.regs[ID_AA64ZFR0] = t; /* Replicate the same data to the 32-bit id registers. */ - u = cpu->isar.id_isar5; + u = cpu->isar.regs[ID_ISAR5]; u = FIELD_DP32(u, ID_ISAR5, AES, 2); /* AES + PMULL */ u = FIELD_DP32(u, ID_ISAR5, SHA1, 1); u = FIELD_DP32(u, ID_ISAR5, SHA2, 1); u = FIELD_DP32(u, ID_ISAR5, CRC32, 1); u = FIELD_DP32(u, ID_ISAR5, RDM, 1); u = FIELD_DP32(u, ID_ISAR5, VCMA, 1); - cpu->isar.id_isar5 = u; + cpu->isar.regs[ID_ISAR5] = u; - u = cpu->isar.id_isar6; + u = cpu->isar.regs[ID_ISAR6]; u = FIELD_DP32(u, ID_ISAR6, JSCVT, 1); u = FIELD_DP32(u, ID_ISAR6, DP, 1); u = FIELD_DP32(u, ID_ISAR6, FHM, 1); @@ -833,39 +833,39 @@ static void aarch64_max_initfn(Object *obj) u = FIELD_DP32(u, ID_ISAR6, SPECRES, 1); u = FIELD_DP32(u, ID_ISAR6, BF16, 1); u = FIELD_DP32(u, ID_ISAR6, I8MM, 1); - cpu->isar.id_isar6 = u; + cpu->isar.regs[ID_ISAR6] = u; - u = cpu->isar.id_pfr0; + u = cpu->isar.regs[ID_PFR0]; u = FIELD_DP32(u, ID_PFR0, DIT, 1); - cpu->isar.id_pfr0 = u; + cpu->isar.regs[ID_PFR0] = u; - u = cpu->isar.id_pfr2; + u = cpu->isar.regs[ID_PFR2]; u = FIELD_DP32(u, ID_PFR2, SSBS, 1); - cpu->isar.id_pfr2 = u; + cpu->isar.regs[ID_PFR2] = u; - u = cpu->isar.id_mmfr3; + u = cpu->isar.regs[ID_MMFR3]; u = FIELD_DP32(u, ID_MMFR3, PAN, 2); /* ATS1E1 */ - cpu->isar.id_mmfr3 = u; + cpu->isar.regs[ID_MMFR3] = u; - u = cpu->isar.id_mmfr4; + u = cpu->isar.regs[ID_MMFR4]; u = FIELD_DP32(u, ID_MMFR4, HPDS, 1); /* AA32HPD */ u = FIELD_DP32(u, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ u = FIELD_DP32(u, ID_MMFR4, CNP, 1); /* TTCNP */ u = FIELD_DP32(u, ID_MMFR4, XNX, 1); /* TTS2UXN */ - cpu->isar.id_mmfr4 = u; + cpu->isar.regs[ID_MMFR4] = u; - t = cpu->isar.id_aa64dfr0; + t = cpu->isar.regs[ID_AA64DFR0]; t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 5); /* v8.4-PMU */ - cpu->isar.id_aa64dfr0 = t; + cpu->isar.regs[ID_AA64DFR0] = t; - u = cpu->isar.id_dfr0; + u = cpu->isar.regs[ID_DFR0]; u = FIELD_DP32(u, ID_DFR0, PERFMON, 5); /* v8.4-PMU */ - cpu->isar.id_dfr0 = u; + cpu->isar.regs[ID_DFR0] = u; - u = cpu->isar.mvfr1; + u = cpu->isar.regs[MVFR1]; u = FIELD_DP32(u, MVFR1, FPHP, 3); /* v8.2-FP16 */ u = FIELD_DP32(u, MVFR1, SIMDHP, 2); /* v8.2-FP16 */ - cpu->isar.mvfr1 = u; + cpu->isar.regs[MVFR1] = u; #ifdef CONFIG_USER_ONLY /* For usermode -cpu max we can use a larger and more efficient DCZ @@ -903,18 +903,18 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->revidr = 0x00000000; cpu->ctr = 0x86668006; cpu->reset_sctlr = 0x30000180; - cpu->isar.id_aa64pfr0 = 0x0000000101111111; /* No RAS Extensions */ - cpu->isar.id_aa64pfr1 = 0x0000000000000000; - cpu->isar.id_aa64dfr0 = 0x0000000010305408; - cpu->isar.id_aa64dfr1 = 0x0000000000000000; + cpu->isar.regs[ID_AA64PFR0] = 0x0000000101111111; /* No RAS Extensions */ + cpu->isar.regs[ID_AA64PFR1] = 0x0000000000000000; + cpu->isar.regs[ID_AA64DFR0] = 0x0000000010305408; + cpu->isar.regs[ID_AA64DFR1] = 0x0000000000000000; cpu->id_aa64afr0 = 0x0000000000000000; cpu->id_aa64afr1 = 0x0000000000000000; - cpu->isar.id_aa64mmfr0 = 0x0000000000001122; - cpu->isar.id_aa64mmfr1 = 0x0000000011212100; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011; - cpu->isar.id_aa64isar0 = 0x0000000010211120; - cpu->isar.id_aa64isar1 = 0x0000000000010001; - cpu->isar.id_aa64zfr0 = 0x0000000000000000; + cpu->isar.regs[ID_AA64MMFR0] = 0x0000000000001122; + cpu->isar.regs[ID_AA64MMFR1] = 0x0000000011212100; + cpu->isar.regs[ID_AA64MMFR2] = 0x0000000000001011; + cpu->isar.regs[ID_AA64ISAR0] = 0x0000000010211120; + cpu->isar.regs[ID_AA64ISAR1] = 0x0000000000010001; + cpu->isar.regs[ID_AA64ZFR0] = 0x0000000000000000; cpu->clidr = 0x0000000080000023; cpu->ccsidr[0] = 0x7007e01c; /* 64KB L1 dcache */ cpu->ccsidr[1] = 0x2007e01c; /* 64KB L1 icache */ diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c index 13d0e9b195..be9c3166fb 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/cpu_tcg.c @@ -65,14 +65,16 @@ static void arm926_initfn(Object *obj) * ARMv5 does not have the ID_ISAR registers, but we can still * set the field to indicate Jazelle support within QEMU. */ - cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + cpu->isar.regs[ID_ISAR1] = FIELD_DP32(cpu->isar.regs[ID_ISAR1], ID_ISAR1, + JAZELLE, 1); /* * Similarly, we need to set MVFR0 fields to enable vfp and short vector * support even though ARMv5 doesn't have this register. */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPSHVEC, + 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPSP, 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPDP, 1); } static void arm946_initfn(Object *obj) @@ -107,14 +109,16 @@ static void arm1026_initfn(Object *obj) * ARMv5 does not have the ID_ISAR registers, but we can still * set the field to indicate Jazelle support within QEMU. */ - cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + cpu->isar.regs[ID_ISAR1] = FIELD_DP32(cpu->isar.regs[ID_ISAR1], ID_ISAR1, + JAZELLE, 1); /* * Similarly, we need to set MVFR0 fields to enable vfp and short vector * support even though ARMv5 doesn't have this register. */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPSHVEC, + 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPSP, 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPDP, 1); { /* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */ @@ -147,22 +151,22 @@ static void arm1136_r2_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); cpu->midr = 0x4107b362; cpu->reset_fpsid = 0x410120b4; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; + cpu->isar.regs[MVFR0] = 0x11111111; + cpu->isar.regs[MVFR1] = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; - cpu->isar.id_dfr0 = 0x2; + cpu->isar.regs[ID_PFR0] = 0x111; + cpu->isar.regs[ID_PFR1] = 0x1; + cpu->isar.regs[ID_DFR0] = 0x2; cpu->id_afr0 = 0x3; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222110; - cpu->isar.id_isar0 = 0x00140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231111; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; + cpu->isar.regs[ID_MMFR0] = 0x01130003; + cpu->isar.regs[ID_MMFR1] = 0x10030302; + cpu->isar.regs[ID_MMFR2] = 0x01222110; + cpu->isar.regs[ID_ISAR0] = 0x00140011; + cpu->isar.regs[ID_ISAR1] = 0x12002111; + cpu->isar.regs[ID_ISAR2] = 0x11231111; + cpu->isar.regs[ID_ISAR3] = 0x01102131; + cpu->isar.regs[ID_ISAR4] = 0x141; cpu->reset_auxcr = 7; } @@ -178,22 +182,22 @@ static void arm1136_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); cpu->midr = 0x4117b363; cpu->reset_fpsid = 0x410120b4; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; + cpu->isar.regs[MVFR0] = 0x11111111; + cpu->isar.regs[MVFR1] = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; - cpu->isar.id_dfr0 = 0x2; + cpu->isar.regs[ID_PFR0] = 0x111; + cpu->isar.regs[ID_PFR1] = 0x1; + cpu->isar.regs[ID_DFR0] = 0x2; cpu->id_afr0 = 0x3; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222110; - cpu->isar.id_isar0 = 0x00140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231111; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; + cpu->isar.regs[ID_MMFR0] = 0x01130003; + cpu->isar.regs[ID_MMFR1] = 0x10030302; + cpu->isar.regs[ID_MMFR2] = 0x01222110; + cpu->isar.regs[ID_ISAR0] = 0x00140011; + cpu->isar.regs[ID_ISAR1] = 0x12002111; + cpu->isar.regs[ID_ISAR2] = 0x11231111; + cpu->isar.regs[ID_ISAR3] = 0x01102131; + cpu->isar.regs[ID_ISAR4] = 0x141; cpu->reset_auxcr = 7; } @@ -210,22 +214,22 @@ static void arm1176_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->midr = 0x410fb767; cpu->reset_fpsid = 0x410120b5; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; + cpu->isar.regs[MVFR0] = 0x11111111; + cpu->isar.regs[MVFR1] = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x11; - cpu->isar.id_dfr0 = 0x33; + cpu->isar.regs[ID_PFR0] = 0x111; + cpu->isar.regs[ID_PFR1] = 0x11; + cpu->isar.regs[ID_DFR0] = 0x33; cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222100; - cpu->isar.id_isar0 = 0x0140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231121; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x01141; + cpu->isar.regs[ID_MMFR0] = 0x01130003; + cpu->isar.regs[ID_MMFR1] = 0x10030302; + cpu->isar.regs[ID_MMFR2] = 0x01222100; + cpu->isar.regs[ID_ISAR0] = 0x0140011; + cpu->isar.regs[ID_ISAR1] = 0x12002111; + cpu->isar.regs[ID_ISAR2] = 0x11231121; + cpu->isar.regs[ID_ISAR3] = 0x01102131; + cpu->isar.regs[ID_ISAR4] = 0x01141; cpu->reset_auxcr = 7; } @@ -240,21 +244,21 @@ static void arm11mpcore_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); cpu->midr = 0x410fb022; cpu->reset_fpsid = 0x410120b4; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; + cpu->isar.regs[MVFR0] = 0x11111111; + cpu->isar.regs[MVFR1] = 0x00000000; cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; - cpu->isar.id_dfr0 = 0; + cpu->isar.regs[ID_PFR0] = 0x111; + cpu->isar.regs[ID_PFR1] = 0x1; + cpu->isar.regs[ID_DFR0] = 0; cpu->id_afr0 = 0x2; - cpu->isar.id_mmfr0 = 0x01100103; - cpu->isar.id_mmfr1 = 0x10020302; - cpu->isar.id_mmfr2 = 0x01222000; - cpu->isar.id_isar0 = 0x00100011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11221011; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; + cpu->isar.regs[ID_MMFR0] = 0x01100103; + cpu->isar.regs[ID_MMFR1] = 0x10020302; + cpu->isar.regs[ID_MMFR2] = 0x01222000; + cpu->isar.regs[ID_ISAR0] = 0x00100011; + cpu->isar.regs[ID_ISAR1] = 0x12002111; + cpu->isar.regs[ID_ISAR2] = 0x11221011; + cpu->isar.regs[ID_ISAR3] = 0x01102131; + cpu->isar.regs[ID_ISAR4] = 0x141; cpu->reset_auxcr = 1; } @@ -278,24 +282,24 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; - cpu->isar.mvfr0 = 0x11110222; - cpu->isar.mvfr1 = 0x00011111; + cpu->isar.regs[MVFR0] = 0x11110222; + cpu->isar.regs[MVFR1] = 0x00011111; cpu->ctr = 0x82048004; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x1031; - cpu->isar.id_pfr1 = 0x11; - cpu->isar.id_dfr0 = 0x400; + cpu->isar.regs[ID_PFR0] = 0x1031; + cpu->isar.regs[ID_PFR1] = 0x11; + cpu->isar.regs[ID_DFR0] = 0x400; cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x31100003; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01202000; - cpu->isar.id_mmfr3 = 0x11; - cpu->isar.id_isar0 = 0x00101111; - cpu->isar.id_isar1 = 0x12112111; - cpu->isar.id_isar2 = 0x21232031; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x00111142; - cpu->isar.dbgdidr = 0x15141000; + cpu->isar.regs[ID_MMFR0] = 0x31100003; + cpu->isar.regs[ID_MMFR1] = 0x20000000; + cpu->isar.regs[ID_MMFR2] = 0x01202000; + cpu->isar.regs[ID_MMFR3] = 0x11; + cpu->isar.regs[ID_ISAR0] = 0x00101111; + cpu->isar.regs[ID_ISAR1] = 0x12112111; + cpu->isar.regs[ID_ISAR2] = 0x21232031; + cpu->isar.regs[ID_ISAR3] = 0x11112131; + cpu->isar.regs[ID_ISAR4] = 0x00111142; + cpu->isar.regs[DBGDIDR] = 0x15141000; cpu->clidr = (1 << 27) | (2 << 24) | 3; cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ @@ -352,24 +356,24 @@ static void cortex_a9_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CBAR); cpu->midr = 0x410fc090; cpu->reset_fpsid = 0x41033090; - cpu->isar.mvfr0 = 0x11110222; - cpu->isar.mvfr1 = 0x01111111; + cpu->isar.regs[MVFR0] = 0x11110222; + cpu->isar.regs[MVFR1] = 0x01111111; cpu->ctr = 0x80038003; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x1031; - cpu->isar.id_pfr1 = 0x11; - cpu->isar.id_dfr0 = 0x000; + cpu->isar.regs[ID_PFR0] = 0x1031; + cpu->isar.regs[ID_PFR1] = 0x11; + cpu->isar.regs[ID_DFR0] = 0x000; cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x00100103; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01230000; - cpu->isar.id_mmfr3 = 0x00002111; - cpu->isar.id_isar0 = 0x00101111; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x00111142; - cpu->isar.dbgdidr = 0x35141000; + cpu->isar.regs[ID_MMFR0] = 0x00100103; + cpu->isar.regs[ID_MMFR1] = 0x20000000; + cpu->isar.regs[ID_MMFR2] = 0x01230000; + cpu->isar.regs[ID_MMFR3] = 0x00002111; + cpu->isar.regs[ID_ISAR0] = 0x00101111; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232041; + cpu->isar.regs[ID_ISAR3] = 0x11112131; + cpu->isar.regs[ID_ISAR4] = 0x00111142; + cpu->isar.regs[DBGDIDR] = 0x35141000; cpu->clidr = (1 << 27) | (1 << 24) | 3; cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */ @@ -417,28 +421,28 @@ static void cortex_a7_initfn(Object *obj) cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7; cpu->midr = 0x410fc075; cpu->reset_fpsid = 0x41023075; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x11111111; + cpu->isar.regs[MVFR0] = 0x10110222; + cpu->isar.regs[MVFR1] = 0x11111111; cpu->ctr = 0x84448003; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x00001131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x02010555; + cpu->isar.regs[ID_PFR0] = 0x00001131; + cpu->isar.regs[ID_PFR1] = 0x00011011; + cpu->isar.regs[ID_DFR0] = 0x02010555; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01240000; - cpu->isar.id_mmfr3 = 0x02102211; + cpu->isar.regs[ID_MMFR0] = 0x10101105; + cpu->isar.regs[ID_MMFR1] = 0x40000000; + cpu->isar.regs[ID_MMFR2] = 0x01240000; + cpu->isar.regs[ID_MMFR3] = 0x02102211; /* * a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but * table 4-41 gives 0x02101110, which includes the arm div insns. */ - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x10011142; - cpu->isar.dbgdidr = 0x3515f005; + cpu->isar.regs[ID_ISAR0] = 0x02101110; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232041; + cpu->isar.regs[ID_ISAR3] = 0x11112131; + cpu->isar.regs[ID_ISAR4] = 0x10011142; + cpu->isar.regs[DBGDIDR] = 0x3515f005; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -463,24 +467,24 @@ static void cortex_a15_initfn(Object *obj) cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; cpu->reset_fpsid = 0x410430f0; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x11111111; + cpu->isar.regs[MVFR0] = 0x10110222; + cpu->isar.regs[MVFR1] = 0x11111111; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x00001131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x02010555; + cpu->isar.regs[ID_PFR0] = 0x00001131; + cpu->isar.regs[ID_PFR1] = 0x00011011; + cpu->isar.regs[ID_DFR0] = 0x02010555; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01240000; - cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x10011142; - cpu->isar.dbgdidr = 0x3515f021; + cpu->isar.regs[ID_MMFR0] = 0x10201105; + cpu->isar.regs[ID_MMFR1] = 0x20000000; + cpu->isar.regs[ID_MMFR2] = 0x01240000; + cpu->isar.regs[ID_MMFR3] = 0x02102211; + cpu->isar.regs[ID_ISAR0] = 0x02101110; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232041; + cpu->isar.regs[ID_ISAR3] = 0x11112131; + cpu->isar.regs[ID_ISAR4] = 0x10011142; + cpu->isar.regs[DBGDIDR] = 0x3515f021; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -504,21 +508,21 @@ static void cortex_m0_initfn(Object *obj) * by looking at ID register fields. We use the same values as * for the M3. */ - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; + cpu->isar.regs[ID_PFR0] = 0x00000030; + cpu->isar.regs[ID_PFR1] = 0x00000200; + cpu->isar.regs[ID_DFR0] = 0x00100000; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + cpu->isar.regs[ID_MMFR0] = 0x00000030; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x00000000; + cpu->isar.regs[ID_MMFR3] = 0x00000000; + cpu->isar.regs[ID_ISAR0] = 0x01141110; + cpu->isar.regs[ID_ISAR1] = 0x02111000; + cpu->isar.regs[ID_ISAR2] = 0x21112231; + cpu->isar.regs[ID_ISAR3] = 0x01111110; + cpu->isar.regs[ID_ISAR4] = 0x01310102; + cpu->isar.regs[ID_ISAR5] = 0x00000000; + cpu->isar.regs[ID_ISAR6] = 0x00000000; } static void cortex_m3_initfn(Object *obj) @@ -529,21 +533,21 @@ static void cortex_m3_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_M_MAIN); cpu->midr = 0x410fc231; cpu->pmsav7_dregion = 8; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; + cpu->isar.regs[ID_PFR0] = 0x00000030; + cpu->isar.regs[ID_PFR1] = 0x00000200; + cpu->isar.regs[ID_DFR0] = 0x00100000; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + cpu->isar.regs[ID_MMFR0] = 0x00000030; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x00000000; + cpu->isar.regs[ID_MMFR3] = 0x00000000; + cpu->isar.regs[ID_ISAR0] = 0x01141110; + cpu->isar.regs[ID_ISAR1] = 0x02111000; + cpu->isar.regs[ID_ISAR2] = 0x21112231; + cpu->isar.regs[ID_ISAR3] = 0x01111110; + cpu->isar.regs[ID_ISAR4] = 0x01310102; + cpu->isar.regs[ID_ISAR5] = 0x00000000; + cpu->isar.regs[ID_ISAR6] = 0x00000000; } static void cortex_m4_initfn(Object *obj) @@ -556,24 +560,24 @@ static void cortex_m4_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fc240; /* r0p0 */ cpu->pmsav7_dregion = 8; - cpu->isar.mvfr0 = 0x10110021; - cpu->isar.mvfr1 = 0x11000011; - cpu->isar.mvfr2 = 0x00000000; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; + cpu->isar.regs[MVFR0] = 0x10110021; + cpu->isar.regs[MVFR1] = 0x11000011; + cpu->isar.regs[MVFR2] = 0x00000000; + cpu->isar.regs[ID_PFR0] = 0x00000030; + cpu->isar.regs[ID_PFR1] = 0x00000200; + cpu->isar.regs[ID_DFR0] = 0x00100000; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + cpu->isar.regs[ID_MMFR0] = 0x00000030; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x00000000; + cpu->isar.regs[ID_MMFR3] = 0x00000000; + cpu->isar.regs[ID_ISAR0] = 0x01141110; + cpu->isar.regs[ID_ISAR1] = 0x02111000; + cpu->isar.regs[ID_ISAR2] = 0x21112231; + cpu->isar.regs[ID_ISAR3] = 0x01111110; + cpu->isar.regs[ID_ISAR4] = 0x01310102; + cpu->isar.regs[ID_ISAR5] = 0x00000000; + cpu->isar.regs[ID_ISAR6] = 0x00000000; } static void cortex_m7_initfn(Object *obj) @@ -586,24 +590,24 @@ static void cortex_m7_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x411fc272; /* r1p2 */ cpu->pmsav7_dregion = 8; - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x12000011; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; + cpu->isar.regs[MVFR0] = 0x10110221; + cpu->isar.regs[MVFR1] = 0x12000011; + cpu->isar.regs[MVFR2] = 0x00000040; + cpu->isar.regs[ID_PFR0] = 0x00000030; + cpu->isar.regs[ID_PFR1] = 0x00000200; + cpu->isar.regs[ID_DFR0] = 0x00100000; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00100030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01101110; - cpu->isar.id_isar1 = 0x02112000; - cpu->isar.id_isar2 = 0x20232231; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + cpu->isar.regs[ID_MMFR0] = 0x00100030; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x01000000; + cpu->isar.regs[ID_MMFR3] = 0x00000000; + cpu->isar.regs[ID_ISAR0] = 0x01101110; + cpu->isar.regs[ID_ISAR1] = 0x02112000; + cpu->isar.regs[ID_ISAR2] = 0x20232231; + cpu->isar.regs[ID_ISAR3] = 0x01111131; + cpu->isar.regs[ID_ISAR4] = 0x01310132; + cpu->isar.regs[ID_ISAR5] = 0x00000000; + cpu->isar.regs[ID_ISAR6] = 0x00000000; } static void cortex_m33_initfn(Object *obj) @@ -618,24 +622,24 @@ static void cortex_m33_initfn(Object *obj) cpu->midr = 0x410fd213; /* r0p3 */ cpu->pmsav7_dregion = 16; cpu->sau_sregion = 8; - cpu->isar.mvfr0 = 0x10110021; - cpu->isar.mvfr1 = 0x11000011; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000210; - cpu->isar.id_dfr0 = 0x00200000; + cpu->isar.regs[MVFR0] = 0x10110021; + cpu->isar.regs[MVFR1] = 0x11000011; + cpu->isar.regs[MVFR2] = 0x00000040; + cpu->isar.regs[ID_PFR0] = 0x00000030; + cpu->isar.regs[ID_PFR1] = 0x00000210; + cpu->isar.regs[ID_DFR0] = 0x00200000; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00101F40; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01101110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + cpu->isar.regs[ID_MMFR0] = 0x00101F40; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x01000000; + cpu->isar.regs[ID_MMFR3] = 0x00000000; + cpu->isar.regs[ID_ISAR0] = 0x01101110; + cpu->isar.regs[ID_ISAR1] = 0x02212000; + cpu->isar.regs[ID_ISAR2] = 0x20232232; + cpu->isar.regs[ID_ISAR3] = 0x01111131; + cpu->isar.regs[ID_ISAR4] = 0x01310132; + cpu->isar.regs[ID_ISAR5] = 0x00000000; + cpu->isar.regs[ID_ISAR6] = 0x00000000; cpu->clidr = 0x00000000; cpu->ctr = 0x8000c000; } @@ -655,24 +659,24 @@ static void cortex_m55_initfn(Object *obj) cpu->pmsav7_dregion = 16; cpu->sau_sregion = 8; /* These are the MVFR* values for the FPU + full MVE configuration */ - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x12100211; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x20000030; - cpu->isar.id_pfr1 = 0x00000230; - cpu->isar.id_dfr0 = 0x10200000; + cpu->isar.regs[MVFR0] = 0x10110221; + cpu->isar.regs[MVFR1] = 0x12100211; + cpu->isar.regs[MVFR2] = 0x00000040; + cpu->isar.regs[ID_PFR0] = 0x20000030; + cpu->isar.regs[ID_PFR1] = 0x00000230; + cpu->isar.regs[ID_DFR0] = 0x10200000; cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00111040; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000011; - cpu->isar.id_isar0 = 0x01103110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + cpu->isar.regs[ID_MMFR0] = 0x00111040; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x01000000; + cpu->isar.regs[ID_MMFR3] = 0x00000011; + cpu->isar.regs[ID_ISAR0] = 0x01103110; + cpu->isar.regs[ID_ISAR1] = 0x02212000; + cpu->isar.regs[ID_ISAR2] = 0x20232232; + cpu->isar.regs[ID_ISAR3] = 0x01111131; + cpu->isar.regs[ID_ISAR4] = 0x01310132; + cpu->isar.regs[ID_ISAR5] = 0x00000000; + cpu->isar.regs[ID_ISAR6] = 0x00000000; cpu->clidr = 0x00000000; /* caches not implemented */ cpu->ctr = 0x8303c003; } @@ -697,21 +701,21 @@ static void cortex_r5_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMSA); set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x411fc153; /* r1p3 */ - cpu->isar.id_pfr0 = 0x0131; - cpu->isar.id_pfr1 = 0x001; - cpu->isar.id_dfr0 = 0x010400; + cpu->isar.regs[ID_PFR0] = 0x0131; + cpu->isar.regs[ID_PFR1] = 0x001; + cpu->isar.regs[ID_DFR0] = 0x010400; cpu->id_afr0 = 0x0; - cpu->isar.id_mmfr0 = 0x0210030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01200000; - cpu->isar.id_mmfr3 = 0x0211; - cpu->isar.id_isar0 = 0x02101111; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232141; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x0010142; - cpu->isar.id_isar5 = 0x0; - cpu->isar.id_isar6 = 0x0; + cpu->isar.regs[ID_MMFR0] = 0x0210030; + cpu->isar.regs[ID_MMFR1] = 0x00000000; + cpu->isar.regs[ID_MMFR2] = 0x01200000; + cpu->isar.regs[ID_MMFR3] = 0x0211; + cpu->isar.regs[ID_ISAR0] = 0x02101111; + cpu->isar.regs[ID_ISAR1] = 0x13112111; + cpu->isar.regs[ID_ISAR2] = 0x21232141; + cpu->isar.regs[ID_ISAR3] = 0x01112131; + cpu->isar.regs[ID_ISAR4] = 0x0010142; + cpu->isar.regs[ID_ISAR5] = 0x0; + cpu->isar.regs[ID_ISAR6] = 0x0; cpu->mp_is_up = true; cpu->pmsav7_dregion = 16; define_arm_cp_regs(cpu, cortexr5_cp_reginfo); @@ -722,8 +726,8 @@ static void cortex_r5f_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cortex_r5_initfn(obj); - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x00000011; + cpu->isar.regs[MVFR0] = 0x10110221; + cpu->isar.regs[MVFR1] = 0x00000011; } static void ti925t_initfn(Object *obj) @@ -942,7 +946,8 @@ static void arm_max_initfn(Object *obj) cortex_a15_initfn(obj); /* old-style VFP short-vector support */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); + cpu->isar.regs[MVFR0] = FIELD_DP32(cpu->isar.regs[MVFR0], MVFR0, FPSHVEC, + 1); #ifdef CONFIG_USER_ONLY /* @@ -954,16 +959,16 @@ static void arm_max_initfn(Object *obj) { uint32_t t; - t = cpu->isar.id_isar5; + t = cpu->isar.regs[ID_ISAR5]; t = FIELD_DP32(t, ID_ISAR5, AES, 2); t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); t = FIELD_DP32(t, ID_ISAR5, RDM, 1); t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); - cpu->isar.id_isar5 = t; + cpu->isar.regs[ID_ISAR5] = t; - t = cpu->isar.id_isar6; + t = cpu->isar.regs[ID_ISAR6]; t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); t = FIELD_DP32(t, ID_ISAR6, DP, 1); t = FIELD_DP32(t, ID_ISAR6, FHM, 1); @@ -971,36 +976,36 @@ static void arm_max_initfn(Object *obj) t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); t = FIELD_DP32(t, ID_ISAR6, BF16, 1); t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); - cpu->isar.id_isar6 = t; + cpu->isar.regs[ID_ISAR6] = t; - t = cpu->isar.mvfr1; + t = cpu->isar.regs[MVFR1]; t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */ t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */ - cpu->isar.mvfr1 = t; + cpu->isar.regs[MVFR1] = t; - t = cpu->isar.mvfr2; + t = cpu->isar.regs[MVFR2]; t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */ t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ - cpu->isar.mvfr2 = t; + cpu->isar.regs[MVFR2] = t; - t = cpu->isar.id_mmfr3; + t = cpu->isar.regs[ID_MMFR3]; t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */ - cpu->isar.id_mmfr3 = t; + cpu->isar.regs[ID_MMFR3] = t; - t = cpu->isar.id_mmfr4; + t = cpu->isar.regs[ID_MMFR4]; t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */ t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* TTCNP */ t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */ - cpu->isar.id_mmfr4 = t; + cpu->isar.regs[ID_MMFR4] = t; - t = cpu->isar.id_pfr0; + t = cpu->isar.regs[ID_PFR0]; t = FIELD_DP32(t, ID_PFR0, DIT, 1); - cpu->isar.id_pfr0 = t; + cpu->isar.regs[ID_PFR0] = t; - t = cpu->isar.id_pfr2; + t = cpu->isar.regs[ID_PFR2]; t = FIELD_DP32(t, ID_PFR2, SSBS, 1); - cpu->isar.id_pfr2 = t; + cpu->isar.regs[ID_PFR2] = t; } #endif /* CONFIG_USER_ONLY */ } diff --git a/target/arm/helper.c b/target/arm/helper.c index 9b317899a6..b8ea1dc1f6 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6547,12 +6547,12 @@ static void define_debug_regs(ARMCPU *cpu) * use AArch32. Given that bit 15 is RES1, if the value is 0 then * the register must not exist for this cpu. */ - if (cpu->isar.dbgdidr != 0) { + if (cpu->isar.regs[DBGDIDR] != 0) { ARMCPRegInfo dbgdidr = { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .accessfn = access_tda, - .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdidr, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.regs[DBGDIDR], }; define_one_arm_cp_reg(cpu, &dbgdidr); } @@ -6707,7 +6707,7 @@ static void define_pmu_regs(ARMCPU *cpu) static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); - uint64_t pfr1 = cpu->isar.id_pfr1; + uint64_t pfr1 = cpu->isar.regs[ID_PFR1]; if (env->gicv3state) { pfr1 |= 1 << 28; @@ -6719,7 +6719,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); - uint64_t pfr0 = cpu->isar.id_aa64pfr0; + uint64_t pfr0 = cpu->isar.regs[ID_AA64PFR0]; if (env->gicv3state) { pfr0 |= 1 << 24; @@ -7501,7 +7501,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_pfr0 }, + .resetvalue = cpu->isar.regs[ID_PFR0] }, /* ID_PFR1 is not a plain ARM_CP_CONST because we don't know * the value of the GIC field until after we define these regs. */ @@ -7515,7 +7515,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_dfr0 }, + .resetvalue = cpu->isar.regs[ID_DFR0] }, { .name = "ID_AFR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -7525,62 +7525,62 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr0 }, + .resetvalue = cpu->isar.regs[ID_MMFR0] }, { .name = "ID_MMFR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr1 }, + .resetvalue = cpu->isar.regs[ID_MMFR1] }, { .name = "ID_MMFR2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr2 }, + .resetvalue = cpu->isar.regs[ID_MMFR2] }, { .name = "ID_MMFR3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr3 }, + .resetvalue = cpu->isar.regs[ID_MMFR3] }, { .name = "ID_ISAR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar0 }, + .resetvalue = cpu->isar.regs[ID_ISAR0] }, { .name = "ID_ISAR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar1 }, + .resetvalue = cpu->isar.regs[ID_ISAR1] }, { .name = "ID_ISAR2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar2 }, + .resetvalue = cpu->isar.regs[ID_ISAR2] }, { .name = "ID_ISAR3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar3 }, + .resetvalue = cpu->isar.regs[ID_ISAR3] }, { .name = "ID_ISAR4", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar4 }, + .resetvalue = cpu->isar.regs[ID_ISAR4] }, { .name = "ID_ISAR5", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar5 }, + .resetvalue = cpu->isar.regs[ID_ISAR5] }, { .name = "ID_MMFR4", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr4 }, + .resetvalue = cpu->isar.regs[ID_MMFR4] }, { .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar6 }, + .resetvalue = cpu->isar.regs[ID_ISAR6] }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v6_idregs); @@ -7630,7 +7630,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, #ifdef CONFIG_USER_ONLY .type = ARM_CP_CONST, - .resetvalue = cpu->isar.id_aa64pfr0 + .resetvalue = cpu->isar.regs[ID_AA64PFR0] #else .type = ARM_CP_NO_RAW, .accessfn = access_aa64_tid3, @@ -7642,7 +7642,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64pfr1}, + .resetvalue = cpu->isar.regs[ID_AA64PFR1]}, { .name = "ID_AA64PFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, @@ -7657,7 +7657,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64zfr0 }, + .resetvalue = cpu->isar.regs[ID_AA64ZFR0] }, { .name = "ID_AA64PFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, @@ -7677,12 +7677,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64dfr0 }, + .resetvalue = cpu->isar.regs[ID_AA64DFR0] }, { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64dfr1 }, + .resetvalue = cpu->isar.regs[ID_AA64DFR1] }, { .name = "ID_AA64DFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, @@ -7717,12 +7717,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64isar0 }, + .resetvalue = cpu->isar.regs[ID_AA64ISAR0] }, { .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64isar1 }, + .resetvalue = cpu->isar.regs[ID_AA64ISAR1] }, { .name = "ID_AA64ISAR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, @@ -7757,17 +7757,17 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr0 }, + .resetvalue = cpu->isar.regs[ID_AA64MMFR0] }, { .name = "ID_AA64MMFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr1 }, + .resetvalue = cpu->isar.regs[ID_AA64MMFR1] }, { .name = "ID_AA64MMFR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr2 }, + .resetvalue = cpu->isar.regs[ID_AA64MMFR2] }, { .name = "ID_AA64MMFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -7797,17 +7797,17 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.mvfr0 }, + .resetvalue = cpu->isar.regs[MVFR0] }, { .name = "MVFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.mvfr1 }, + .resetvalue = cpu->isar.regs[MVFR1] }, { .name = "MVFR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.mvfr2 }, + .resetvalue = cpu->isar.regs[MVFR2] }, { .name = "MVFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -7817,7 +7817,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_pfr2 }, + .resetvalue = cpu->isar.regs[ID_PFR2] }, { .name = "MVFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 0dc96560d3..66ad698df1 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -449,15 +449,15 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) int reg; uint64_t *val; } regs[] = { - { HV_SYS_REG_ID_AA64PFR0_EL1, &host_isar.id_aa64pfr0 }, - { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.id_aa64pfr1 }, - { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.id_aa64dfr0 }, - { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.id_aa64dfr1 }, - { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.id_aa64isar0 }, - { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.id_aa64isar1 }, - { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.id_aa64mmfr0 }, - { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.id_aa64mmfr1 }, - { HV_SYS_REG_ID_AA64MMFR2_EL1, &host_isar.id_aa64mmfr2 }, + { HV_SYS_REG_ID_AA64PFR0_EL1, &host_isar.regs[ID_AA64PFR0] }, + { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.regs[ID_AA64PFR1] }, + { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.regs[ID_AA64DFR0] }, + { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.regs[ID_AA64DFR1] }, + { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.regs[ID_AA64ISAR0] }, + { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.regs[ID_AA64ISAR1] }, + { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.regs[ID_AA64MMFR0] }, + { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.regs[ID_AA64MMFR1] }, + { HV_SYS_REG_ID_AA64MMFR2_EL1, &host_isar.regs[ID_AA64MMFR2] }, }; hv_vcpu_t fd; hv_return_t r = HV_SUCCESS; @@ -593,7 +593,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) /* We're limited to underlying hardware caps, override internal versions */ ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, HV_SYS_REG_ID_AA64MMFR0_EL1, - &arm_cpu->isar.id_aa64mmfr0); + &arm_cpu->isar.regs[ID_AA64MMFR0]); assert_hvf_ok(ret); return 0; diff --git a/target/arm/internals.h b/target/arm/internals.h index 89f7610ebc..0ea225e480 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -254,7 +254,7 @@ static inline unsigned int arm_pamax(ARMCPU *cpu) [5] = 48, }; unsigned int parange = - FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + FIELD_EX64(cpu->isar.regs[ID_AA64MMFR0], ID_AA64MMFR0, PARANGE); /* id_aa64mmfr0 is a read-only register so values outside of the * supported mappings can be considered an implementation error. */ @@ -808,9 +808,9 @@ static inline uint32_t arm_debug_exception_fsr(CPUARMState *env) static inline int arm_num_brps(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, BRPS) + 1; + return FIELD_EX64(cpu->isar.regs[ID_AA64DFR0], ID_AA64DFR0, BRPS) + 1; } else { - return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, BRPS) + 1; + return FIELD_EX32(cpu->isar.regs[DBGDIDR], DBGDIDR, BRPS) + 1; } } @@ -822,9 +822,9 @@ static inline int arm_num_brps(ARMCPU *cpu) static inline int arm_num_wrps(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, WRPS) + 1; + return FIELD_EX64(cpu->isar.regs[ID_AA64DFR0], ID_AA64DFR0, WRPS) + 1; } else { - return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, WRPS) + 1; + return FIELD_EX32(cpu->isar.regs[DBGDIDR], DBGDIDR, WRPS) + 1; } } @@ -836,9 +836,9 @@ static inline int arm_num_wrps(ARMCPU *cpu) static inline int arm_num_ctx_cmps(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS) + 1; + return FIELD_EX64(cpu->isar.regs[ID_AA64DFR0], ID_AA64DFR0, CTX_CMPS) + 1; } else { - return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, CTX_CMPS) + 1; + return FIELD_EX32(cpu->isar.regs[DBGDIDR], DBGDIDR, CTX_CMPS) + 1; } } diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index e790d6c9a5..4f97e516c2 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -468,7 +468,7 @@ void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa) } } -static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) +static int read_sys_reg32(int fd, uint64_t *pret, uint64_t id) { uint64_t ret; struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)&ret }; @@ -528,7 +528,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->target = init.target; ahcf->dtb_compatible = "arm,arm-v8"; - err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, + err = read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64PFR0], ARM64_SYS_REG(3, 0, 0, 4, 0)); if (unlikely(err < 0)) { /* @@ -547,24 +547,24 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * ??? Either of these sounds like too much effort just * to work around running a modern host kernel. */ - ahcf->isar.id_aa64pfr0 = 0x00000011; /* EL1&0, AArch64 only */ + ahcf->isar.regs[ID_AA64PFR0] = 0x00000011; /* EL1&0, AArch64 only */ err = 0; } else { - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr1, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64PFR1], ARM64_SYS_REG(3, 0, 0, 4, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr0, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64DFR0], ARM64_SYS_REG(3, 0, 0, 5, 0)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr1, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64DFR1], ARM64_SYS_REG(3, 0, 0, 5, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar0, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64ISAR0], ARM64_SYS_REG(3, 0, 0, 6, 0)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar1, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64ISAR1], ARM64_SYS_REG(3, 0, 0, 6, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64MMFR0], ARM64_SYS_REG(3, 0, 0, 7, 0)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr1, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64MMFR1], ARM64_SYS_REG(3, 0, 0, 7, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr2, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64MMFR2], ARM64_SYS_REG(3, 0, 0, 7, 2)); /* @@ -574,44 +574,44 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * than skipping the reads and leaving 0, as we must avoid * considering the values in every case. */ - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr0, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_PFR0], ARM64_SYS_REG(3, 0, 0, 1, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr1, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_PFR1], ARM64_SYS_REG(3, 0, 0, 1, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr2, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_PFR2], ARM64_SYS_REG(3, 0, 0, 3, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_DFR0], ARM64_SYS_REG(3, 0, 0, 1, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_MMFR0], ARM64_SYS_REG(3, 0, 0, 1, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_MMFR1], ARM64_SYS_REG(3, 0, 0, 1, 5)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr2, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_MMFR2], ARM64_SYS_REG(3, 0, 0, 1, 6)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_MMFR3], ARM64_SYS_REG(3, 0, 0, 1, 7)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR0], ARM64_SYS_REG(3, 0, 0, 2, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR1], ARM64_SYS_REG(3, 0, 0, 2, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR2], ARM64_SYS_REG(3, 0, 0, 2, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR3], ARM64_SYS_REG(3, 0, 0, 2, 3)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR4], ARM64_SYS_REG(3, 0, 0, 2, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR5], ARM64_SYS_REG(3, 0, 0, 2, 5)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_MMFR4], ARM64_SYS_REG(3, 0, 0, 2, 6)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[ID_ISAR6], ARM64_SYS_REG(3, 0, 0, 2, 7)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[MVFR0], ARM64_SYS_REG(3, 0, 0, 3, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[MVFR1], ARM64_SYS_REG(3, 0, 0, 3, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, + err |= read_sys_reg32(fdarray[2], &ahcf->isar.regs[MVFR2], ARM64_SYS_REG(3, 0, 0, 3, 2)); /* @@ -624,14 +624,17 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * arch/arm64/kvm/sys_regs.c:trap_dbgidr() does. * We only do this if the CPU supports AArch32 at EL1. */ - if (FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL1) >= 2) { - int wrps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, WRPS); - int brps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, BRPS); + if (FIELD_EX32(ahcf->isar.regs[ID_AA64PFR0], ID_AA64PFR0, EL1) >= 2) { + int wrps = FIELD_EX64(ahcf->isar.regs[ID_AA64DFR0], ID_AA64DFR0, + WRPS); + int brps = FIELD_EX64(ahcf->isar.regs[ID_AA64DFR0], ID_AA64DFR0, + BRPS); int ctx_cmps = - FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS); + FIELD_EX64(ahcf->isar.regs[ID_AA64DFR0], ID_AA64DFR0, + CTX_CMPS); int version = 6; /* ARMv8 debug architecture */ bool has_el3 = - !!FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL3); + !!FIELD_EX32(ahcf->isar.regs[ID_AA64PFR0], ID_AA64PFR0, EL3); uint32_t dbgdidr = 0; dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, WRPS, wrps); @@ -641,7 +644,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, NSUHD_IMP, has_el3); dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, SE_IMP, has_el3); dbgdidr |= (1 << 15); /* RES1 bit */ - ahcf->isar.dbgdidr = dbgdidr; + ahcf->isar.regs[DBGDIDR] = dbgdidr; } } @@ -649,9 +652,9 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) /* Add feature bits that can't appear until after VCPU init. */ if (sve_supported) { - t = ahcf->isar.id_aa64pfr0; + t = ahcf->isar.regs[ID_AA64PFR0]; t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); - ahcf->isar.id_aa64pfr0 = t; + ahcf->isar.regs[ID_AA64PFR0] = t; /* * Before v5.1, KVM did not support SVE and did not expose @@ -659,7 +662,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * not expose the register to "user" requests like this * unless the host supports SVE. */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64zfr0, + err |= read_sys_reg64(fdarray[2], &ahcf->isar.regs[ID_AA64ZFR0], ARM64_SYS_REG(3, 0, 0, 4, 4)); } -- Gitee From abd51f8d46b916efb37cb2c8face176bc83c0d5d Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:35 +0800 Subject: [PATCH 125/486] target/arm: parse cpu feature related options The implementation of CPUClass::parse_features only supports CPU features in "feature=value" format. However, libvirt maybe send us a CPU feature string in "+feature/-feature" format. Hence, we need to override CPUClass::parse_features to support CPU feature string in both "feature=value" and "+feature/-feature" format. The logic of AArch64CPUClass::parse_features is similar to that of X86CPUClass::parse_features. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu64.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 96a49a3158..9e5179afbe 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -982,6 +982,88 @@ static gchar *aarch64_gdb_arch_name(CPUState *cs) return g_strdup("aarch64"); } +static void +cpu_add_feat_as_prop(const char *typename, const char *name, const char *val) +{ + GlobalProperty *prop = g_new0(typeof(*prop), 1); + prop->driver = typename; + prop->property = g_strdup(name); + prop->value = g_strdup(val); + qdev_prop_register_global(prop); +} + +static gint compare_string(gconstpointer a, gconstpointer b) +{ + return g_strcmp0(a, b); +} + +static GList *plus_features, *minus_features; + +static void aarch64_cpu_parse_features(const char *typename, char *features, + Error **errp) +{ + GList *l; + char *featurestr; /* Single 'key=value" string being parsed */ + static bool cpu_globals_initialized; + + if (cpu_globals_initialized) { + return; + } + cpu_globals_initialized = true; + + if (!features) { + return; + } + for (featurestr = strtok(features, ","); + featurestr; + featurestr = strtok(NULL, ",")) { + const char *name; + const char *val = NULL; + char *eq = NULL; + + /* Compatibility syntax: */ + if (featurestr[0] == '+') { + plus_features = g_list_append(plus_features, + g_strdup(featurestr + 1)); + continue; + } else if (featurestr[0] == '-') { + minus_features = g_list_append(minus_features, + g_strdup(featurestr + 1)); + continue; + } + + eq = strchr(featurestr, '='); + name = featurestr; + if (eq) { + *eq++ = 0; + val = eq; + } else { + error_setg(errp, "Unsupported property format: %s", name); + return; + } + + if (g_list_find_custom(plus_features, name, compare_string)) { + warn_report("Ambiguous CPU model string. " + "Don't mix both \"+%s\" and \"%s=%s\"", + name, name, val); + } + if (g_list_find_custom(minus_features, name, compare_string)) { + warn_report("Ambiguous CPU model string. " + "Don't mix both \"-%s\" and \"%s=%s\"", + name, name, val); + } + cpu_add_feat_as_prop(typename, name, val); + } + + for (l = plus_features; l; l = l->next) { + cpu_add_feat_as_prop(typename, l->data, "on"); + } + + for (l = minus_features; l; l = l->next) { + cpu_add_feat_as_prop(typename, l->data, "off"); + } +} + static void aarch64_cpu_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); @@ -991,6 +1073,7 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 34; cc->gdb_core_xml_file = "aarch64-core.xml"; cc->gdb_arch_name = aarch64_gdb_arch_name; + cc->parse_features = aarch64_cpu_parse_features; object_class_property_add_bool(oc, "aarch64", aarch64_cpu_get_aarch64, aarch64_cpu_set_aarch64); -- Gitee From 9fd09aabba558b39ca949ef376d05bc0779fdda6 Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:37 +0800 Subject: [PATCH 126/486] target/arm: register CPU features for property The Arm architecture specifies a number of ID registers that are characterized as comprising a set of 4-bit ID fields. Each ID field identifies the presence, and possibly the level of support for, a particular feature in an implementation of the architecture. [1] For most of the ID fields, there is a minimum presence value, equal to or higher than which means the corresponding CPU feature is implemented. Hence, we can use the minimum presence value to determine whether a CPU feature is enabled and enable a CPU feature. To disable a CPU feature, setting the corresponding ID field to 0x0/0xf (for unsigned/signed field) seems as a good idea. However, it maybe lead to some problems. For example, ID_AA64PFR0_EL1.FP is a signed ID field. ID_AA64PFR0_EL1.FP == 0x0 represents the implementation of FP (floating-point) and ID_AA64PFR0_EL1.FP == 0x1 represents the implementation of FPHP (half-precision floating-point). If ID_AA64PFR0_EL1.FP is set to 0xf when FPHP is disabled (which is also disable FP), guest kernel maybe stuck. Hence, we add a ni_value (means not-implemented value) to disable a CPU feature safely. [1] D13.1.3 Principles of the ID scheme for fields in ID registers in DDI.0487 Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu.c | 343 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f1ce0474a3..c081ecc12b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1211,6 +1211,347 @@ unsigned int gt_cntfrq_period_ns(ARMCPU *cpu) NANOSECONDS_PER_SECOND / cpu->gt_cntfrq_hz : 1; } +/** + * CPUFeatureInfo: + * @reg: The ID register where the ID field is in. + * @name: The name of the CPU feature. + * @length: The bit length of the ID field. + * @shift: The bit shift of the ID field in the ID register. + * @min_value: The minimum value equal to or larger than which means the CPU + * feature is implemented. + * @ni_value: Not-implemented value. It will be set to the ID field when + * disabling the CPU feature. Usually, it's min_value - 1. + * @sign: Whether the ID field is signed. + * @is_32bit: Whether the CPU feature is for 32-bit. + * + * In ARM, a CPU feature is described by an ID field, which is a 4-bit field in + * an ID register. + */ +typedef struct CPUFeatureInfo { + CPUIDReg reg; + const char *name; + int length; + int shift; + int min_value; + int ni_value; + bool sign; + bool is_32bit; +} CPUFeatureInfo; + +#define FIELD_INFO(feature_name, id_reg, field, s, min_val, ni_val, is32bit) { \ + .reg = id_reg, \ + .length = R_ ## id_reg ## _ ## field ## _LENGTH, \ + .shift = R_ ## id_reg ## _ ## field ## _SHIFT, \ + .sign = s, \ + .min_value = min_val, \ + .ni_value = ni_val, \ + .name = feature_name, \ + .is_32bit = is32bit, \ +} + +static struct CPUFeatureInfo cpu_features[] = { + FIELD_INFO("swap", ID_ISAR0, SWAP, false, 1, 0, true), + FIELD_INFO("bitcount", ID_ISAR0, BITCOUNT, false, 1, 0, true), + FIELD_INFO("bitfield", ID_ISAR0, BITFIELD, false, 1, 0, true), + FIELD_INFO("cmpbranch", ID_ISAR0, CMPBRANCH, false, 1, 0, true), + FIELD_INFO("coproc", ID_ISAR0, COPROC, false, 1, 0, true), + FIELD_INFO("debug", ID_ISAR0, DEBUG, false, 1, 0, true), + FIELD_INFO("device", ID_ISAR0, DIVIDE, false, 1, 0, true), + + FIELD_INFO("endian", ID_ISAR1, ENDIAN, false, 1, 0, true), + FIELD_INFO("except", ID_ISAR1, EXCEPT, false, 1, 0, true), + FIELD_INFO("except_ar", ID_ISAR1, EXCEPT_AR, false, 1, 0, true), + FIELD_INFO("extend", ID_ISAR1, EXTEND, false, 1, 0, true), + FIELD_INFO("ifthen", ID_ISAR1, IFTHEN, false, 1, 0, true), + FIELD_INFO("immediate", ID_ISAR1, IMMEDIATE, false, 1, 0, true), + FIELD_INFO("interwork", ID_ISAR1, INTERWORK, false, 1, 0, true), + FIELD_INFO("jazelle", ID_ISAR1, JAZELLE, false, 1, 0, true), + + FIELD_INFO("loadstore", ID_ISAR2, LOADSTORE, false, 1, 0, true), + FIELD_INFO("memhint", ID_ISAR2, MEMHINT, false, 1, 0, true), + FIELD_INFO("multiaccessint", ID_ISAR2, MULTIACCESSINT, false, 1, 0, true), + FIELD_INFO("mult", ID_ISAR2, MULT, false, 1, 0, true), + FIELD_INFO("mults", ID_ISAR2, MULTS, false, 1, 0, true), + FIELD_INFO("multu", ID_ISAR2, MULTU, false, 1, 0, true), + FIELD_INFO("psr_ar", ID_ISAR2, PSR_AR, false, 1, 0, true), + FIELD_INFO("reversal", ID_ISAR2, REVERSAL, false, 1, 0, true), + + FIELD_INFO("saturate", ID_ISAR3, SATURATE, false, 1, 0, true), + FIELD_INFO("simd", ID_ISAR3, SIMD, false, 1, 0, true), + FIELD_INFO("svc", ID_ISAR3, SVC, false, 1, 0, true), + FIELD_INFO("synchprim", ID_ISAR3, SYNCHPRIM, false, 1, 0, true), + FIELD_INFO("tabbranch", ID_ISAR3, TABBRANCH, false, 1, 0, true), + FIELD_INFO("t32copy", ID_ISAR3, T32COPY, false, 1, 0, true), + FIELD_INFO("truenop", ID_ISAR3, TRUENOP, false, 1, 0, true), + FIELD_INFO("t32ee", ID_ISAR3, T32EE, false, 1, 0, true), + + FIELD_INFO("unpriv", ID_ISAR4, UNPRIV, false, 1, 0, true), + FIELD_INFO("withshifts", ID_ISAR4, WITHSHIFTS, false, 1, 0, true), + FIELD_INFO("writeback", ID_ISAR4, WRITEBACK, false, 1, 0, true), + FIELD_INFO("smc", ID_ISAR4, SMC, false, 1, 0, true), + FIELD_INFO("barrier", ID_ISAR4, BARRIER, false, 1, 0, true), + FIELD_INFO("synchprim_frac", ID_ISAR4, SYNCHPRIM_FRAC, false, 1, 0, true), + FIELD_INFO("psr_m", ID_ISAR4, PSR_M, false, 1, 0, true), + FIELD_INFO("swp_frac", ID_ISAR4, SWP_FRAC, false, 1, 0, true), + + FIELD_INFO("sevl", ID_ISAR5, SEVL, false, 1, 0, true), + FIELD_INFO("aes", ID_ISAR5, AES, false, 1, 0, true), + FIELD_INFO("sha1", ID_ISAR5, SHA1, false, 1, 0, true), + FIELD_INFO("sha2", ID_ISAR5, SHA2, false, 1, 0, true), + FIELD_INFO("crc32", ID_ISAR5, CRC32, false, 1, 0, true), + FIELD_INFO("rdm", ID_ISAR5, RDM, false, 1, 0, true), + FIELD_INFO("vcma", ID_ISAR5, VCMA, false, 1, 0, true), + + FIELD_INFO("jscvt", ID_ISAR6, JSCVT, false, 1, 0, true), + FIELD_INFO("dp", ID_ISAR6, DP, false, 1, 0, true), + FIELD_INFO("fhm", ID_ISAR6, FHM, false, 1, 0, true), + FIELD_INFO("sb", ID_ISAR6, SB, false, 1, 0, true), + FIELD_INFO("specres", ID_ISAR6, SPECRES, false, 1, 0, true), + + FIELD_INFO("cmaintva", ID_MMFR3, CMAINTVA, false, 1, 0, true), + FIELD_INFO("cmaintsw", ID_MMFR3, CMAINTSW, false, 1, 0, true), + FIELD_INFO("bpmaint", ID_MMFR3, BPMAINT, false, 1, 0, true), + FIELD_INFO("maintbcst", ID_MMFR3, MAINTBCST, false, 1, 0, true), + FIELD_INFO("pan", ID_MMFR3, PAN, false, 1, 0, true), + FIELD_INFO("cohwalk", ID_MMFR3, COHWALK, false, 1, 0, true), + FIELD_INFO("cmemsz", ID_MMFR3, CMEMSZ, false, 1, 0, true), + FIELD_INFO("supersec", ID_MMFR3, SUPERSEC, false, 1, 0, true), + + FIELD_INFO("specsei", ID_MMFR4, SPECSEI, false, 1, 0, true), + FIELD_INFO("ac2", ID_MMFR4, AC2, false, 1, 0, true), + FIELD_INFO("xnx", ID_MMFR4, XNX, false, 1, 0, true), + FIELD_INFO("cnp", ID_MMFR4, CNP, false, 1, 0, true), + FIELD_INFO("hpds", ID_MMFR4, HPDS, false, 1, 0, true), + FIELD_INFO("lsm", ID_MMFR4, LSM, false, 1, 0, true), + FIELD_INFO("ccidx", ID_MMFR4, CCIDX, false, 1, 0, true), + FIELD_INFO("evt", ID_MMFR4, EVT, false, 1, 0, true), + + FIELD_INFO("simdreg", MVFR0, SIMDREG, false, 1, 0, true), + FIELD_INFO("fpsp", MVFR0, FPSP, false, 1, 0, true), + FIELD_INFO("fpdp", MVFR0, FPDP, false, 1, 0, true), + FIELD_INFO("fptrap", MVFR0, FPTRAP, false, 1, 0, true), + FIELD_INFO("fpdivide", MVFR0, FPDIVIDE, false, 1, 0, true), + FIELD_INFO("fpsqrt", MVFR0, FPSQRT, false, 1, 0, true), + FIELD_INFO("fpshvec", MVFR0, FPSHVEC, false, 1, 0, true), + FIELD_INFO("fpround", MVFR0, FPROUND, false, 1, 0, true), + + FIELD_INFO("fpftz", MVFR1, FPFTZ, false, 1, 0, true), + FIELD_INFO("fpdnan", MVFR1, FPDNAN, false, 1, 0, true), + FIELD_INFO("simdls", MVFR1, SIMDLS, false, 1, 0, true), + FIELD_INFO("simdint", MVFR1, SIMDINT, false, 1, 0, true), + FIELD_INFO("simdsp", MVFR1, SIMDSP, false, 1, 0, true), + FIELD_INFO("simdhp", MVFR1, SIMDHP, false, 1, 0, true), + FIELD_INFO("fphp", MVFR1, FPHP, false, 1, 0, true), + FIELD_INFO("simdfmac", MVFR1, SIMDFMAC, false, 1, 0, true), + + FIELD_INFO("simdmisc", MVFR2, SIMDMISC, false, 1, 0, true), + FIELD_INFO("fpmisc", MVFR2, FPMISC, false, 1, 0, true), + + FIELD_INFO("debugver", ID_AA64DFR0, DEBUGVER, false, 1, 0, false), + FIELD_INFO("tracever", ID_AA64DFR0, TRACEVER, false, 1, 0, false), + FIELD_INFO("pmuver", ID_AA64DFR0, PMUVER, false, 1, 0, false), + FIELD_INFO("brps", ID_AA64DFR0, BRPS, false, 1, 0, false), + FIELD_INFO("wrps", ID_AA64DFR0, WRPS, false, 1, 0, false), + FIELD_INFO("ctx_cmps", ID_AA64DFR0, CTX_CMPS, false, 1, 0, false), + FIELD_INFO("pmsver", ID_AA64DFR0, PMSVER, false, 1, 0, false), + FIELD_INFO("doublelock", ID_AA64DFR0, DOUBLELOCK, false, 1, 0, false), + FIELD_INFO("tracefilt", ID_AA64DFR0, TRACEFILT, false, 1, 0, false), + + FIELD_INFO("aes", ID_AA64ISAR0, AES, false, 1, 0, false), + FIELD_INFO("sha1", ID_AA64ISAR0, SHA1, false, 1, 0, false), + FIELD_INFO("sha2", ID_AA64ISAR0, SHA2, false, 1, 0, false), + FIELD_INFO("crc32", ID_AA64ISAR0, CRC32, false, 1, 0, false), + FIELD_INFO("atomics", ID_AA64ISAR0, ATOMIC, false, 1, 0, false), + FIELD_INFO("asimdrdm", ID_AA64ISAR0, RDM, false, 1, 0, false), + FIELD_INFO("sha3", ID_AA64ISAR0, SHA3, false, 1, 0, false), + FIELD_INFO("sm3", ID_AA64ISAR0, SM3, false, 1, 0, false), + FIELD_INFO("sm4", ID_AA64ISAR0, SM4, false, 1, 0, false), + FIELD_INFO("asimddp", ID_AA64ISAR0, DP, false, 1, 0, false), + FIELD_INFO("asimdfhm", ID_AA64ISAR0, FHM, false, 1, 0, false), + FIELD_INFO("flagm", ID_AA64ISAR0, TS, false, 1, 0, false), + FIELD_INFO("tlb", ID_AA64ISAR0, TLB, false, 1, 0, false), + FIELD_INFO("rng", ID_AA64ISAR0, RNDR, false, 1, 0, false), + + FIELD_INFO("dcpop", ID_AA64ISAR1, DPB, false, 1, 0, false), + FIELD_INFO("papa", ID_AA64ISAR1, APA, false, 1, 0, false), + FIELD_INFO("api", ID_AA64ISAR1, API, false, 1, 0, false), + FIELD_INFO("jscvt", ID_AA64ISAR1, JSCVT, false, 1, 0, false), + FIELD_INFO("fcma", ID_AA64ISAR1, FCMA, false, 1, 0, false), + FIELD_INFO("lrcpc", ID_AA64ISAR1, LRCPC, false, 1, 0, false), + FIELD_INFO("pacg", ID_AA64ISAR1, GPA, false, 1, 0, false), + FIELD_INFO("gpi", ID_AA64ISAR1, GPI, false, 1, 0, false), + FIELD_INFO("frint", ID_AA64ISAR1, FRINTTS, false, 1, 0, false), + FIELD_INFO("sb", ID_AA64ISAR1, SB, false, 1, 0, false), + FIELD_INFO("specres", ID_AA64ISAR1, SPECRES, false, 1, 0, false), + + FIELD_INFO("el0", ID_AA64PFR0, EL0, false, 1, 0, false), + FIELD_INFO("el1", ID_AA64PFR0, EL1, false, 1, 0, false), + FIELD_INFO("el2", ID_AA64PFR0, EL2, false, 1, 0, false), + FIELD_INFO("el3", ID_AA64PFR0, EL3, false, 1, 0, false), + FIELD_INFO("fp", ID_AA64PFR0, FP, true, 0, 0xf, false), + FIELD_INFO("asimd", ID_AA64PFR0, ADVSIMD, true, 0, 0xf, false), + FIELD_INFO("gic", ID_AA64PFR0, GIC, false, 1, 0, false), + FIELD_INFO("ras", ID_AA64PFR0, RAS, false, 1, 0, false), + FIELD_INFO("sve", ID_AA64PFR0, SVE, false, 1, 0, false), + + FIELD_INFO("bti", ID_AA64PFR1, BT, false, 1, 0, false), + FIELD_INFO("ssbs", ID_AA64PFR1, SSBS, false, 1, 0, false), + FIELD_INFO("mte", ID_AA64PFR1, MTE, false, 1, 0, false), + FIELD_INFO("ras_frac", ID_AA64PFR1, RAS_FRAC, false, 1, 0, false), + + FIELD_INFO("parange", ID_AA64MMFR0, PARANGE, false, 1, 0, false), + FIELD_INFO("asidbits", ID_AA64MMFR0, ASIDBITS, false, 1, 0, false), + FIELD_INFO("bigend", ID_AA64MMFR0, BIGEND, false, 1, 0, false), + FIELD_INFO("snsmem", ID_AA64MMFR0, SNSMEM, false, 1, 0, false), + FIELD_INFO("bigendel0", ID_AA64MMFR0, BIGENDEL0, false, 1, 0, false), + FIELD_INFO("tgran16", ID_AA64MMFR0, TGRAN16, false, 1, 0, false), + FIELD_INFO("tgran64", ID_AA64MMFR0, TGRAN64, false, 1, 0, false), + FIELD_INFO("tgran4", ID_AA64MMFR0, TGRAN4, false, 1, 0, false), + FIELD_INFO("tgran16_2", ID_AA64MMFR0, TGRAN16_2, false, 1, 0, false), + FIELD_INFO("tgran64_2", ID_AA64MMFR0, TGRAN64_2, false, 1, 0, false), + FIELD_INFO("tgran4_2", ID_AA64MMFR0, TGRAN4_2, false, 1, 0, false), + FIELD_INFO("exs", ID_AA64MMFR0, EXS, false, 1, 0, false), + + FIELD_INFO("hafdbs", ID_AA64MMFR1, HAFDBS, false, 1, 0, false), + FIELD_INFO("vmidbits", ID_AA64MMFR1, VMIDBITS, false, 1, 0, false), + FIELD_INFO("vh", ID_AA64MMFR1, VH, false, 1, 0, false), + FIELD_INFO("hpds", ID_AA64MMFR1, HPDS, false, 1, 0, false), + FIELD_INFO("lo", ID_AA64MMFR1, LO, false, 1, 0, false), + FIELD_INFO("pan", ID_AA64MMFR1, PAN, false, 1, 0, false), + FIELD_INFO("specsei", ID_AA64MMFR1, SPECSEI, false, 1, 0, false), + FIELD_INFO("xnx", ID_AA64MMFR1, XNX, false, 1, 0, false), + + FIELD_INFO("cnp", ID_AA64MMFR2, CNP, false, 1, 0, false), + FIELD_INFO("uao", ID_AA64MMFR2, UAO, false, 1, 0, false), + FIELD_INFO("lsm", ID_AA64MMFR2, LSM, false, 1, 0, false), + FIELD_INFO("iesb", ID_AA64MMFR2, IESB, false, 1, 0, false), + FIELD_INFO("varange", ID_AA64MMFR2, VARANGE, false, 1, 0, false), + FIELD_INFO("ccidx", ID_AA64MMFR2, CCIDX, false, 1, 0, false), + FIELD_INFO("nv", ID_AA64MMFR2, NV, false, 1, 0, false), + FIELD_INFO("st", ID_AA64MMFR2, ST, false, 1, 0, false), + FIELD_INFO("uscat", ID_AA64MMFR2, AT, false, 1, 0, false), + FIELD_INFO("ids", ID_AA64MMFR2, IDS, false, 1, 0, false), + FIELD_INFO("fwb", ID_AA64MMFR2, FWB, false, 1, 0, false), + FIELD_INFO("ttl", ID_AA64MMFR2, TTL, false, 1, 0, false), + FIELD_INFO("bbm", ID_AA64MMFR2, BBM, false, 1, 0, false), + FIELD_INFO("evt", ID_AA64MMFR2, EVT, false, 1, 0, false), + FIELD_INFO("e0pd", ID_AA64MMFR2, E0PD, false, 1, 0, false), + + FIELD_INFO("copdbg", ID_DFR0, COPDBG, false, 1, 0, false), + FIELD_INFO("copsdbg", ID_DFR0, COPSDBG, false, 1, 0, false), + FIELD_INFO("mmapdbg", ID_DFR0, MMAPDBG, false, 1, 0, false), + FIELD_INFO("coptrc", ID_DFR0, COPTRC, false, 1, 0, false), + FIELD_INFO("mmaptrc", ID_DFR0, MMAPTRC, false, 1, 0, false), + FIELD_INFO("mprofdbg", ID_DFR0, MPROFDBG, false, 1, 0, false), + FIELD_INFO("perfmon", ID_DFR0, PERFMON, false, 1, 0, false), + FIELD_INFO("tracefilt", ID_DFR0, TRACEFILT, false, 1, 0, false), + + { + .reg = ID_AA64PFR0, .length = R_ID_AA64PFR0_FP_LENGTH, + .shift = R_ID_AA64PFR0_FP_SHIFT, .sign = true, .min_value = 1, + .ni_value = 0, .name = "fphp", .is_32bit = false, + }, + { + .reg = ID_AA64PFR0, .length = R_ID_AA64PFR0_ADVSIMD_LENGTH, + .shift = R_ID_AA64PFR0_ADVSIMD_SHIFT, .sign = true, .min_value = 1, + .ni_value = 0, .name = "asimdhp", .is_32bit = false, + }, + { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_AES_LENGTH, + .shift = R_ID_AA64ISAR0_AES_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "pmull", .is_32bit = false, + }, + { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_SHA2_LENGTH, + .shift = R_ID_AA64ISAR0_SHA2_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "sha512", .is_32bit = false, + }, + { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_TS_LENGTH, + .shift = R_ID_AA64ISAR0_TS_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "flagm2", .is_32bit = false, + }, + { + .reg = ID_AA64ISAR1, .length = R_ID_AA64ISAR1_DPB_LENGTH, + .shift = R_ID_AA64ISAR1_DPB_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "dcpodp", .is_32bit = false, + }, + { + .reg = ID_AA64ISAR1, .length = R_ID_AA64ISAR1_LRCPC_LENGTH, + .shift = R_ID_AA64ISAR1_LRCPC_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "ilrcpc", .is_32bit = false, + }, +}; + +static void arm_cpu_get_bit_prop(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + CPUFeatureInfo *feat = opaque; + int field_value = feat->sign ? sextract64(cpu->isar.regs[feat->reg], + feat->shift, feat->length) : + extract64(cpu->isar.regs[feat->reg], + feat->shift, feat->length); + bool value = field_value >= feat->min_value; + + visit_type_bool(v, name, &value, errp); +} + +static void arm_cpu_set_bit_prop(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; + CPUFeatureInfo *feat = opaque; + Error *local_err = NULL; + bool value; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_bool(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (value) { + isar->regs[feat->reg] = deposit64(isar->regs[feat->reg], + feat->shift, feat->length, + feat->min_value); + } else { + isar->regs[feat->reg] = deposit64(isar->regs[feat->reg], + feat->shift, feat->length, + feat->ni_value); + } +} + +static void arm_cpu_register_feature_props(ARMCPU *cpu) +{ + int i; + int num = ARRAY_SIZE(cpu_features); + ObjectProperty *op; + CPUARMState *env = &cpu->env; + + for (i = 0; i < num; i++) { + if ((arm_feature(env, ARM_FEATURE_AARCH64) && cpu_features[i].is_32bit) + || (!arm_feature(env, ARM_FEATURE_AARCH64) && + cpu_features[i].is_32bit)) { + continue; + } + op = object_property_find(OBJECT(cpu), cpu_features[i].name); + if (!op) { + object_property_add(OBJECT(cpu), cpu_features[i].name, "bool", + arm_cpu_get_bit_prop, + arm_cpu_set_bit_prop, + NULL, &cpu_features[i]); + } + } +} + void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -1319,6 +1660,8 @@ void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); + arm_cpu_register_feature_props(cpu); + if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) { qdev_property_add_static(DEVICE(cpu), &arm_cpu_gt_cntfrq_property); } -- Gitee From 0272c52e36ab95389e665ca19b129178b0b46eac Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:40 +0800 Subject: [PATCH 127/486] target/arm: Allow ID registers to synchronize to KVM There are 2 steps to synchronize the values of system registers from CPU state to KVM: 1. write to the values of system registers from CPU state to (index,value) list by write_cpustate_to_list; 2. write the values in (index,value) list to KVM by write_list_to_kvmstate; In step 1, the values of constant system registers are not allowed to write to (index,value) list. However, a constant system register is CONSTANT for guest but not for QEMU, which means, QEMU can set/modify the value of constant system registers that is different from phsical registers when startup. But if KVM is enabled, guest can not read the values of the system registers which QEMU set unless they can be written to (index,value) list. And why not try to write to KVM if kvm_sync is true? At the moment we call write_cpustate_to_list, all ID registers are contant, including ID_PFR1_EL1 and ID_AA64PFR0_EL1 because GIC has been initialized. Hence, let's give all ID registers a chance to write to KVM. If the write is successful, then write to (index,value) list. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/helper.c | 31 ++++++++++++++++++++----------- target/arm/kvm.c | 38 ++++++++++++++++++++++++++++++++++++++ target/arm/kvm_arm.h | 3 +++ 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index b8ea1dc1f6..79f77705c3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -35,6 +35,7 @@ #include "exec/cpu_ldst.h" #include "semihosting/common-semi.h" #endif +#include "kvm_arm.h" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ #define PMCR_NUM_COUNTERS 4 /* QEMU IMPDEF choice */ @@ -149,30 +150,38 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) ok = false; continue; } - if (ri->type & ARM_CP_NO_RAW) { + /* + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2), + * where 1<=crm<8, 0<=op2<8. Let's give ID registers a chance to + * synchronize to kvm. + */ + if ((ri->type & ARM_CP_NO_RAW) && !(kvm_sync && + ri->opc0 == 3 && ri->opc1 == 0 && ri->crn == 0 && ri->crm > 0)) { continue; } newval = read_raw_cp_reg(&cpu->env, ri); if (kvm_sync) { - /* - * Only sync if the previous list->cpustate sync succeeded. - * Rather than tracking the success/failure state for every - * item in the list, we just recheck "does the raw write we must - * have made in write_list_to_cpustate() read back OK" here. - */ - uint64_t oldval = cpu->cpreg_values[i]; + /* Only sync if we can sync to KVM successfully. */ + uint64_t oldval; + uint64_t kvmval; + if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &oldval)) { + continue; + } if (oldval == newval) { continue; } - write_raw_cp_reg(&cpu->env, ri, oldval); - if (read_raw_cp_reg(&cpu->env, ri) != oldval) { + if (kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &newval)) { + continue; + } + if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &kvmval) || + kvmval != newval) { continue; } - write_raw_cp_reg(&cpu->env, ri, newval); + kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &oldval); } cpu->cpreg_values[i] = newval; } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index bbf1ce7ba3..59d556724f 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -514,6 +514,44 @@ out: return ret; } +int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *target) +{ + uint32_t v32; + int ret; + + switch (regidx & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + ret = kvm_get_one_reg(CPU(cpu), regidx, &v32); + if (ret == 0) { + *target = v32; + } + return ret; + case KVM_REG_SIZE_U64: + return kvm_get_one_reg(CPU(cpu), regidx, target); + default: + return -1; + } +} + +int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *source) +{ + uint32_t v32; + + switch (regidx & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + v32 = *source; + if (v32 != *source) { + error_report("the value of source is too large"); + return -1; + } + return kvm_set_one_reg(CPU(cpu), regidx, &v32); + case KVM_REG_SIZE_U64: + return kvm_set_one_reg(CPU(cpu), regidx, source); + default: + return -1; + } +} + bool write_kvmstate_to_list(ARMCPU *cpu) { CPUState *cs = CPU(cpu); diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index b7f78b5215..f8e0e64363 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -528,4 +528,7 @@ static inline const char *its_class_name(void) } } +int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *target); +int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *source); + #endif -- Gitee From 632d58d1b908ee979074b589417f446c0a3be35d Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:46 +0800 Subject: [PATCH 128/486] target/arm: introduce CPU feature dependency mechanism Some CPU features are dependent on other CPU features. For example, ID_AA64PFR0_EL1.FP field and ID_AA64PFR0_EL1.AdvSIMD must have the same value, which means FP and ADVSIMD are dependent on each other, FPHP and ADVSIMDHP are dependent on each other. This commit introduces a mechanism for CPU feature dependency in AArch64. We build a directed graph from the CPU feature dependency relationship, each edge from->to means the `to` CPU feature is dependent on the `from` CPU feature. And we will automatically enable/disable CPU feature according to the directed graph. For example, a, b, and c CPU features are in relationship a->b->c, which means c is dependent on b and b is dependent on a. If c is enabled by user, then a and b is enabled automatically. And if a is disabled by user, then b and c is disabled automatically. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c081ecc12b..ee09642dae 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1483,6 +1483,103 @@ static struct CPUFeatureInfo cpu_features[] = { }, }; +typedef struct CPUFeatureDep { + CPUFeatureInfo from, to; +} CPUFeatureDep; + +static const CPUFeatureDep feature_dependencies[] = { + { + .from = FIELD_INFO("fp", ID_AA64PFR0, FP, true, 0, 0xf, false), + .to = FIELD_INFO("asimd", ID_AA64PFR0, ADVSIMD, true, 0, 0xf, false), + }, + { + .from = FIELD_INFO("asimd", ID_AA64PFR0, ADVSIMD, true, 0, 0xf, false), + .to = FIELD_INFO("fp", ID_AA64PFR0, FP, true, 0, 0xf, false), + }, + { + .from = { + .reg = ID_AA64PFR0, .length = R_ID_AA64PFR0_FP_LENGTH, + .shift = R_ID_AA64PFR0_FP_SHIFT, .sign = true, .min_value = 1, + .ni_value = 0, .name = "fphp", .is_32bit = false, + }, + .to = { + .reg = ID_AA64PFR0, .length = R_ID_AA64PFR0_ADVSIMD_LENGTH, + .shift = R_ID_AA64PFR0_ADVSIMD_SHIFT, .sign = true, .min_value = 1, + .ni_value = 0, .name = "asimdhp", .is_32bit = false, + }, + }, + { + .from = { + .reg = ID_AA64PFR0, .length = R_ID_AA64PFR0_ADVSIMD_LENGTH, + .shift = R_ID_AA64PFR0_ADVSIMD_SHIFT, .sign = true, .min_value = 1, + .ni_value = 0, .name = "asimdhp", .is_32bit = false, + }, + .to = { + .reg = ID_AA64PFR0, .length = R_ID_AA64PFR0_FP_LENGTH, + .shift = R_ID_AA64PFR0_FP_SHIFT, .sign = true, .min_value = 1, + .ni_value = 0, .name = "fphp", .is_32bit = false, + }, + }, + { + + .from = FIELD_INFO("aes", ID_AA64ISAR0, AES, false, 1, 0, false), + .to = { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_AES_LENGTH, + .shift = R_ID_AA64ISAR0_AES_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "pmull", .is_32bit = false, + }, + }, + { + + .from = FIELD_INFO("sha2", ID_AA64ISAR0, SHA2, false, 1, 0, false), + .to = { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_SHA2_LENGTH, + .shift = R_ID_AA64ISAR0_SHA2_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "sha512", .is_32bit = false, + }, + }, + { + .from = FIELD_INFO("lrcpc", ID_AA64ISAR1, LRCPC, false, 1, 0, false), + .to = { + .reg = ID_AA64ISAR1, .length = R_ID_AA64ISAR1_LRCPC_LENGTH, + .shift = R_ID_AA64ISAR1_LRCPC_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "ilrcpc", .is_32bit = false, + }, + }, + { + .from = FIELD_INFO("sm3", ID_AA64ISAR0, SM3, false, 1, 0, false), + .to = FIELD_INFO("sm4", ID_AA64ISAR0, SM4, false, 1, 0, false), + }, + { + .from = FIELD_INFO("sm4", ID_AA64ISAR0, SM4, false, 1, 0, false), + .to = FIELD_INFO("sm3", ID_AA64ISAR0, SM3, false, 1, 0, false), + }, + { + .from = FIELD_INFO("sha1", ID_AA64ISAR0, SHA1, false, 1, 0, false), + .to = FIELD_INFO("sha2", ID_AA64ISAR0, SHA2, false, 1, 0, false), + }, + { + .from = FIELD_INFO("sha1", ID_AA64ISAR0, SHA1, false, 1, 0, false), + .to = FIELD_INFO("sha3", ID_AA64ISAR0, SHA3, false, 1, 0, false), + }, + { + .from = FIELD_INFO("sha3", ID_AA64ISAR0, SHA3, false, 1, 0, false), + .to = { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_SHA2_LENGTH, + .shift = R_ID_AA64ISAR0_SHA2_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "sha512", .is_32bit = false, + }, + }, + { + .from = { + .reg = ID_AA64ISAR0, .length = R_ID_AA64ISAR0_SHA2_LENGTH, + .shift = R_ID_AA64ISAR0_SHA2_SHIFT, .sign = false, .min_value = 2, + .ni_value = 1, .name = "sha512", .is_32bit = false, + }, + .to = FIELD_INFO("sha3", ID_AA64ISAR0, SHA3, false, 1, 0, false), + }, +}; + static void arm_cpu_get_bit_prop(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -1519,13 +1616,45 @@ static void arm_cpu_set_bit_prop(Object *obj, Visitor *v, const char *name, } if (value) { + if (object_property_get_bool(obj, feat->name, NULL)) { + return; + } isar->regs[feat->reg] = deposit64(isar->regs[feat->reg], feat->shift, feat->length, feat->min_value); + /* Auto enable the features which current feature is dependent on. */ + for (int i = 0; i < ARRAY_SIZE(feature_dependencies); ++i) { + const CPUFeatureDep *d = &feature_dependencies[i]; + if (strcmp(d->to.name, feat->name) != 0) { + continue; + } + + object_property_set_bool(obj, d->from.name, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } } else { + if (!object_property_get_bool(obj, feat->name, NULL)) { + return; + } isar->regs[feat->reg] = deposit64(isar->regs[feat->reg], feat->shift, feat->length, feat->ni_value); + /* Auto disable the features which are dependent on current feature. */ + for (int i = 0; i < ARRAY_SIZE(feature_dependencies); ++i) { + const CPUFeatureDep *d = &feature_dependencies[i]; + if (strcmp(d->from.name, feat->name) != 0) { + continue; + } + + object_property_set_bool(obj, d->to.name, false, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } } } -- Gitee From 536aa9ecc3cb25c81c2df56230c690257189d4ef Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:55 +0800 Subject: [PATCH 129/486] target/arm: introduce KVM_CAP_ARM_CPU_FEATURE Introduce KVM_CAP_ARM_CPU_FEATURE to check whether KVM supports to set CPU features in ARM. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- linux-headers/linux/kvm.h | 2 ++ target/arm/cpu.c | 5 +++++ target/arm/kvm64.c | 14 ++++++++++++++ target/arm/kvm_arm.h | 7 +++++++ 4 files changed, 28 insertions(+) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index bcaf66cc4d..5d8e42b8f8 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1113,6 +1113,8 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204 #define KVM_CAP_ARM_MTE 205 +#define KVM_CAP_ARM_CPU_FEATURE 555 + #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing_irqchip { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ee09642dae..3024f4a3f5 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1604,6 +1604,11 @@ static void arm_cpu_set_bit_prop(Object *obj, Visitor *v, const char *name, Error *local_err = NULL; bool value; + if (!kvm_arm_cpu_feature_supported()) { + warn_report("KVM doesn't support to set CPU feature in arm. " + "Setting to `%s` is ignored.", name); + return; + } if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); return; diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 4f97e516c2..b34a87fd24 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -827,6 +827,20 @@ static int kvm_arm_sve_set_vls(CPUState *cs) return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); } +bool kvm_arm_cpu_feature_supported(void) +{ + static bool cpu_feature_initialized; + static bool cpu_feature_supported; + + if (!cpu_feature_initialized) { + cpu_feature_supported = kvm_check_extension(kvm_state, + KVM_CAP_ARM_CPU_FEATURE); + cpu_feature_initialized = true; + } + + return cpu_feature_supported; +} + #define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 int kvm_arch_init_vcpu(CPUState *cs) diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index f8e0e64363..82145607ec 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -306,6 +306,13 @@ bool kvm_arm_pmu_supported(void); */ bool kvm_arm_sve_supported(void); +/** + * kvm_arm_cpu_feature_supported: + * + * Returns true if KVM can set CPU features and false otherwise. + */ +bool kvm_arm_cpu_feature_supported(void); + /** * kvm_arm_get_max_vm_ipa_size: * @ms: Machine state handle -- Gitee From e6dd7faeea77206d7e6589cbb54ad43926457052 Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 6 Aug 2020 16:14:58 +0800 Subject: [PATCH 130/486] target/arm: Add CPU features to query-cpu-model-expansion Add CPU features to the result of query-cpu-model-expansion so that other applications (such as libvirt) can know the supported CPU features. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu.c | 27 +++++++++++++++++++++++++++ target/arm/cpu.h | 2 ++ target/arm/monitor.c | 2 ++ 3 files changed, 31 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 3024f4a3f5..2d6a26336f 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -25,6 +25,8 @@ #include "qemu/module.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include "qapi/qmp/qdict.h" +#include "qom/qom-qobject.h" #include "cpu.h" #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" @@ -1580,6 +1582,31 @@ static const CPUFeatureDep feature_dependencies[] = { }, }; +void arm_cpu_features_to_dict(ARMCPU *cpu, QDict *features) +{ + Object *obj = OBJECT(cpu); + const char *name; + ObjectProperty *prop; + bool is_32bit = !arm_feature(&cpu->env, ARM_FEATURE_AARCH64); + int i; + + for (i = 0; i < ARRAY_SIZE(cpu_features); ++i) { + if (is_32bit != cpu_features[i].is_32bit) { + continue; + } + + name = cpu_features[i].name; + prop = object_property_find(obj, name); + if (prop) { + QObject *value; + + assert(prop->get); + value = object_property_get_qobject(obj, name, &error_abort); + qdict_put_obj(features, name, value); + } + } +} + static void arm_cpu_get_bit_prop(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 3dda33f347..947897d5ac 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -4398,4 +4398,6 @@ static inline bool isar_feature_any_tts2uxn(const ARMISARegisters *id) #define cpu_isar_feature(name, cpu) \ ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) +void arm_cpu_features_to_dict(ARMCPU *cpu, QDict *features); + #endif diff --git a/target/arm/monitor.c b/target/arm/monitor.c index 80c64fa355..4c6f1181d9 100644 --- a/target/arm/monitor.c +++ b/target/arm/monitor.c @@ -217,6 +217,8 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, } } + arm_cpu_features_to_dict(ARM_CPU(obj), qdict_out); + if (!qdict_size(qdict_out)) { qobject_unref(qdict_out); } else { -- Gitee From 85d5b46d8225c5875b8b3ff68967d46bcde9a549 Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Tue, 11 Aug 2020 10:28:10 +0800 Subject: [PATCH 131/486] target/arm: Add more CPU features Add i8mm, bf16, and dgh CPU features for AArch64. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2d6a26336f..1c1647a0a8 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1309,6 +1309,9 @@ static struct CPUFeatureInfo cpu_features[] = { FIELD_INFO("fhm", ID_ISAR6, FHM, false, 1, 0, true), FIELD_INFO("sb", ID_ISAR6, SB, false, 1, 0, true), FIELD_INFO("specres", ID_ISAR6, SPECRES, false, 1, 0, true), + FIELD_INFO("i8mm", ID_AA64ISAR1, I8MM, false, 1, 0, false), + FIELD_INFO("bf16", ID_AA64ISAR1, BF16, false, 1, 0, false), + FIELD_INFO("dgh", ID_AA64ISAR1, DGH, false, 1, 0, false), FIELD_INFO("cmaintva", ID_MMFR3, CMAINTVA, false, 1, 0, true), FIELD_INFO("cmaintsw", ID_MMFR3, CMAINTSW, false, 1, 0, true), -- Gitee From 4558dc5590b89b1252baea2734c2b3668566e5cb Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Mon, 7 Sep 2020 14:07:07 +0800 Subject: [PATCH 132/486] target/arm: ignore evtstrm and cpuid CPU features evtstrm and cpuid cann't be controlled by VMM: 1. evtstrm: The generic timer is configured to generate events at a frequency of approximately 100KHz. It's controlled by the linux kernel config CONFIG_ARM_ARCH_TIMER_EVTSTREAM. 2. cpuid: EL0 access to certain ID registers is available. It's always set by linux kernel after 77c97b4ee2129 ("arm64: cpufeature: Expose CPUID registers by emulation"). However, they are exposed by getauxval() and /proc/cpuinfo. Hence, let's report and ignore the CPU features if someone set them. Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu64.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 9e5179afbe..287e7ac91c 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -982,10 +982,37 @@ static gchar *aarch64_gdb_arch_name(CPUState *cs) return g_strdup("aarch64"); } +static const char *unconfigurable_feats[] = { + "evtstrm", + "cpuid", + NULL +}; + +static bool is_configurable_feat(const char *name) +{ + int i; + + for (i = 0; unconfigurable_feats[i]; ++i) { + if (g_strcmp0(unconfigurable_feats[i], name) == 0) { + return false; + } + } + + return true; +} + static void cpu_add_feat_as_prop(const char *typename, const char *name, const char *val) { - GlobalProperty *prop = g_new0(typeof(*prop), 1); + GlobalProperty *prop; + + if (!is_configurable_feat(name)) { + info_report("CPU feature '%s' is not configurable by QEMU. Ignore it.", + name); + return; + } + + prop = g_new0(typeof(*prop), 1); prop->driver = typename; prop->property = g_strdup(name); prop->value = g_strdup(val); -- Gitee From 3371917ea92265377f87692a717397267416c4aa Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Wed, 16 Sep 2020 19:40:28 +0800 Subject: [PATCH 133/486] target/arm: only set ID_PFR1_EL1.GIC for AArch32 guest Some AArch64 CPU doesn't support AArch32 mode, and the values of AArch32 registers are all 0. Hence, We'd better not to modify AArch32 registers in AArch64 mode. Signed-off-by: zhanghailiang Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 79f77705c3..4c7b4cadfa 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6718,7 +6718,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) ARMCPU *cpu = env_archcpu(env); uint64_t pfr1 = cpu->isar.regs[ID_PFR1]; - if (env->gicv3state) { + if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && env->gicv3state) { pfr1 |= 1 << 28; } return pfr1; -- Gitee From 9680adfba5ca871f69a6fbd15b92571fc2a52d78 Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Wed, 9 Dec 2020 19:35:08 +0800 Subject: [PATCH 134/486] target/arm: Fix write redundant values to kvm After modifying the value of a ID register, we'd better to try to write it to KVM so that we can known the value is acceptable for KVM. Because it may modify the registers' values of KVM, it's not suitable for other registers. (cherry-picked from a0d7a9de807639fcfcbe1fe037cb8772d459a9cf) Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/helper.c | 73 ++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 4c7b4cadfa..1dd5d64d96 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -134,6 +134,16 @@ static bool raw_accessors_invalid(const ARMCPRegInfo *ri) return true; } +static bool is_id_reg(const ARMCPRegInfo *ri) +{ + /* + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2), + * where 1<=crm<8, 0<=op2<8. + */ + return ri->opc0 == 3 && ri->opc1 == 0 && ri->crn == 0 && + ri->crm > 0 && ri->crm < 8; +} + bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) { /* Write the coprocessor state from cpu->env to the (index,value) list. */ @@ -150,38 +160,53 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) ok = false; continue; } - /* - * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2), - * where 1<=crm<8, 0<=op2<8. Let's give ID registers a chance to - * synchronize to kvm. - */ - if ((ri->type & ARM_CP_NO_RAW) && !(kvm_sync && - ri->opc0 == 3 && ri->opc1 == 0 && ri->crn == 0 && ri->crm > 0)) { + if ((ri->type & ARM_CP_NO_RAW) && !(kvm_sync && is_id_reg(ri))) { continue; } newval = read_raw_cp_reg(&cpu->env, ri); if (kvm_sync) { - /* Only sync if we can sync to KVM successfully. */ - uint64_t oldval; - uint64_t kvmval; + if (is_id_reg(ri)) { + /* Only sync if we can sync to KVM successfully. */ + uint64_t oldval; + uint64_t kvmval; - if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &oldval)) { - continue; - } - if (oldval == newval) { - continue; - } + if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &oldval)) { + continue; + } + if (oldval == newval) { + continue; + } - if (kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &newval)) { - continue; - } - if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &kvmval) || - kvmval != newval) { - continue; - } + if (kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &newval)) { + continue; + } + if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &kvmval) || + kvmval != newval) { + continue; + } + + kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &oldval); + } else { + /* + * Only sync if the previous list->cpustate sync succeeded. + * Rather than tracking the success/failure state for every + * item in the list, we just recheck "does the raw write we must + * have made in write_list_to_cpustate() read back OK" here. + */ + uint64_t oldval = cpu->cpreg_values[i]; + + if (oldval == newval) { + continue; + } - kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &oldval); + write_raw_cp_reg(&cpu->env, ri, oldval); + if (read_raw_cp_reg(&cpu->env, ri) != oldval) { + continue; + } + + write_raw_cp_reg(&cpu->env, ri, newval); + } } cpu->cpreg_values[i] = newval; } -- Gitee From 20bd52038a960e0c959af38a5d3d7a6601db8e8b Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Mon, 21 Sep 2020 22:14:20 +0800 Subject: [PATCH 135/486] target/arm: clear EL2 and EL3 only when kvm is not enabled When has_el2 and has_el3 are disabled, which is the default value for virt machine, QEMU will clear the corresponding field in ID_PFR1_EL1 and ID_AA64PFR0_EL1 to not expose EL3 and EL2 to guest. Because KVM doesn't support to emulate ID registers in AArch64 before, it will not take effect. Hence, clear EL2 and EL3 only when kvm is not enabled for backwards compatibility. Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1c1647a0a8..65163f5135 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2283,7 +2283,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } - if (!arm_feature(env, ARM_FEATURE_M) && !cpu->has_el3) { + if (!arm_feature(env, ARM_FEATURE_M) && !cpu->has_el3 && !kvm_enabled()) { /* If the has_el3 CPU property is disabled then we need to disable the * feature. */ @@ -2324,7 +2324,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->pmceid1 = 0; } - if (!arm_feature(env, ARM_FEATURE_EL2)) { + if (!arm_feature(env, ARM_FEATURE_EL2) && !kvm_enabled()) { /* Disable the hypervisor feature bits in the processor feature * registers if we don't have EL2. These are id_pfr1[15:12] and * id_aa64pfr0_el1[11:8]. -- Gitee From e2cb8b57278357c0a42cf7722b8c28b6f8d7585c Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Sat, 19 Sep 2020 09:04:45 +0800 Subject: [PATCH 136/486] target/arm: Update the ID registers of Kunpeng-920 The values of some ID registers in Kunpeng-920 are not exactly correct. Let's update them. The values are read from Kunpeng-920 by calling read_sysreg_s. Signed-off-by: Peng Liang Signed-off-by: Dongxu Sun --- target/arm/cpu64.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 287e7ac91c..3ec788fc29 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -262,10 +262,33 @@ static void aarch64_kunpeng_920_initfn(Object *obj) cpu->midr = 0x480fd010; cpu->ctr = 0x84448004; - cpu->isar.regs[ID_AA64PFR0] = 0x11001111; + cpu->isar.regs[ID_ISAR0] = 0; + cpu->isar.regs[ID_ISAR1] = 0; + cpu->isar.regs[ID_ISAR2] = 0; + cpu->isar.regs[ID_ISAR3] = 0; + cpu->isar.regs[ID_ISAR4] = 0; + cpu->isar.regs[ID_ISAR5] = 0; + cpu->isar.regs[ID_MMFR0] = 0; + cpu->isar.regs[ID_MMFR1] = 0; + cpu->isar.regs[ID_MMFR2] = 0; + cpu->isar.regs[ID_MMFR3] = 0; + cpu->isar.regs[ID_MMFR4] = 0; + cpu->isar.regs[MVFR0] = 0; + cpu->isar.regs[MVFR1] = 0; + cpu->isar.regs[MVFR2] = 0; + cpu->isar.regs[ID_DFR0] = 0; + cpu->isar.regs[MVFR2] = 0; + cpu->isar.regs[MVFR2] = 0; + cpu->isar.regs[MVFR2] = 0; + cpu->isar.regs[ID_PFR0] = 0; + cpu->isar.regs[ID_PFR1] = 0; + cpu->isar.regs[ID_AA64PFR0] = 0x0000010011111111; cpu->isar.regs[ID_AA64DFR0] = 0x110305408; - cpu->isar.regs[ID_AA64ISAR0] = 0x10211120; + cpu->isar.regs[ID_AA64ISAR0] = 0x0001100010211120; + cpu->isar.regs[ID_AA64ISAR1] = 0x00011001; cpu->isar.regs[ID_AA64MMFR0] = 0x101125; + cpu->isar.regs[ID_AA64MMFR1] = 0x10211122; + cpu->isar.regs[ID_AA64MMFR2] = 0x00001011; } void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) -- Gitee From 93f01916f0c1e11f38edb8ccc4118c940d9c089f Mon Sep 17 00:00:00 2001 From: eillon Date: Tue, 8 Feb 2022 22:43:59 -0500 Subject: [PATCH 137/486] hw/usb: reduce the vpcu cost of UHCI when VNC disconnect Reduce the vpcu cost by set a lower FRAME_TIMER_FREQ of the UHCI when VNC client disconnected. This can reduce about 3% cost of vcpu thread. Signed-off-by: eillon --- hw/usb/core.c | 5 ++-- hw/usb/desc.c | 7 +++-- hw/usb/dev-hid.c | 2 +- hw/usb/hcd-uhci.c | 63 ++++++++++++++++++++++++++++++++++------ hw/usb/hcd-uhci.h | 1 + hw/usb/host-libusb.c | 32 ++++++++++++++++++++ include/hw/usb.h | 1 + include/qemu/timer.h | 28 ++++++++++++++++++ ui/vnc.c | 4 +++ util/qemu-timer.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 197 insertions(+), 15 deletions(-) diff --git a/hw/usb/core.c b/hw/usb/core.c index 975f76250a..51b36126ca 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -87,7 +87,7 @@ void usb_device_reset(USBDevice *dev) return; } usb_device_handle_reset(dev); - dev->remote_wakeup = 0; + dev->remote_wakeup &= ~USB_DEVICE_REMOTE_WAKEUP; dev->addr = 0; dev->state = USB_STATE_DEFAULT; } @@ -105,7 +105,8 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) */ return; } - if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { + if ((dev->remote_wakeup & USB_DEVICE_REMOTE_WAKEUP) + && dev->port && dev->port->ops->wakeup) { dev->port->ops->wakeup(dev->port); } if (bus->ops->wakeup_endpoint) { diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 8b6eaea407..78bbe74c71 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -751,7 +751,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, if (config->bmAttributes & USB_CFG_ATT_SELFPOWER) { data[0] |= 1 << USB_DEVICE_SELF_POWERED; } - if (dev->remote_wakeup) { + if (dev->remote_wakeup & USB_DEVICE_REMOTE_WAKEUP) { data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; } data[1] = 0x00; @@ -761,14 +761,15 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, } case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; + dev->remote_wakeup &= ~USB_DEVICE_REMOTE_WAKEUP; ret = 0; } trace_usb_clear_device_feature(dev->addr, value, ret); break; case DeviceOutRequest | USB_REQ_SET_FEATURE: + dev->remote_wakeup |= USB_DEVICE_REMOTE_WAKEUP_IS_SUPPORTED; if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; + dev->remote_wakeup |= USB_DEVICE_REMOTE_WAKEUP; ret = 0; } trace_usb_set_device_feature(dev->addr, value, ret); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 1c7ae97c30..9fb89f6955 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -745,7 +745,7 @@ static int usb_ptr_post_load(void *opaque, int version_id) { USBHIDState *s = opaque; - if (s->dev.remote_wakeup) { + if (s->dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP) { hid_pointer_activate(&s->hid); } return 0; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index d1b5657d72..693c68f445 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -44,6 +44,8 @@ #include "hcd-uhci.h" #define FRAME_TIMER_FREQ 1000 +#define FRAME_TIMER_FREQ_LAZY 10 +#define USB_DEVICE_NEED_NORMAL_FREQ "QEMU USB Tablet" #define FRAME_MAX_LOOPS 256 @@ -111,6 +113,22 @@ static void uhci_async_cancel(UHCIAsync *async); static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td); static void uhci_resume(void *opaque); +static int64_t uhci_frame_timer_freq = FRAME_TIMER_FREQ_LAZY; + +static void uhci_set_frame_freq(int freq) +{ + if (freq <= 0) { + return; + } + + uhci_frame_timer_freq = freq; +} + +static qemu_usb_controller qemu_uhci = { + .name = "uhci", + .qemu_set_freq = uhci_set_frame_freq, +}; + static inline int32_t uhci_queue_token(UHCI_TD *td) { if ((td->token & (0xf << 15)) == 0) { @@ -353,7 +371,7 @@ static int uhci_post_load(void *opaque, int version_id) if (version_id < 2) { s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); + (NANOSECONDS_PER_SECOND / uhci_frame_timer_freq); } return 0; } @@ -394,8 +412,29 @@ static void uhci_port_write(void *opaque, hwaddr addr, if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ trace_usb_uhci_schedule_start(); - s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); + + /* + * If the frequency of frame_timer is too slow, Guest OS (Win2012) would become + * blue-screen after hotplugging some vcpus. + * If this USB device support the remote-wakeup, the UHCI controller + * will enter global suspend mode when there is no input for several seconds. + * In this case, Qemu will delete the frame_timer. Since the frame_timer has been deleted, + * there is no influence to the performance of Vms. So, we can change the frequency to 1000. + * After that the frequency will be safe when we trigger the frame_timer again. + * Excepting this, there are two ways to change the frequency: + * 1)VNC connect/disconnect;2)attach/detach USB device. + */ + if ((uhci_frame_timer_freq != FRAME_TIMER_FREQ) + && (s->ports[0].port.dev) + && (!memcmp(s->ports[0].port.dev->product_desc, + USB_DEVICE_NEED_NORMAL_FREQ, strlen(USB_DEVICE_NEED_NORMAL_FREQ))) + && (s->ports[0].port.dev->remote_wakeup & USB_DEVICE_REMOTE_WAKEUP_IS_SUPPORTED)) { + qemu_log("turn up the frequency of UHCI controller to %d\n", FRAME_TIMER_FREQ); + uhci_frame_timer_freq = FRAME_TIMER_FREQ; + } + + s->frame_time = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ; + s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->frame_time; timer_mod(s->frame_timer, s->expire_time); s->status &= ~UHCI_STS_HCHALTED; } else if (!(val & UHCI_CMD_RS)) { @@ -1083,7 +1122,6 @@ static void uhci_frame_timer(void *opaque) UHCIState *s = opaque; uint64_t t_now, t_last_run; int i, frames; - const uint64_t frame_t = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ; s->completions_only = false; qemu_bh_cancel(s->bh); @@ -1099,14 +1137,14 @@ static void uhci_frame_timer(void *opaque) } /* We still store expire_time in our state, for migration */ - t_last_run = s->expire_time - frame_t; + t_last_run = s->expire_time - s->frame_time; t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); /* Process up to MAX_FRAMES_PER_TICK frames */ - frames = (t_now - t_last_run) / frame_t; + frames = (t_now - t_last_run) / s->frame_time; if (frames > s->maxframes) { int skipped = frames - s->maxframes; - s->expire_time += skipped * frame_t; + s->expire_time += skipped * s->frame_time; s->frnum = (s->frnum + skipped) & 0x7ff; frames -= skipped; } @@ -1123,7 +1161,7 @@ static void uhci_frame_timer(void *opaque) /* The spec says frnum is the frame currently being processed, and * the guest must look at frnum - 1 on interrupt, so inc frnum now */ s->frnum = (s->frnum + 1) & 0x7ff; - s->expire_time += frame_t; + s->expire_time += s->frame_time; } /* Complete the previous frame(s) */ @@ -1134,7 +1172,12 @@ static void uhci_frame_timer(void *opaque) } s->pending_int_mask = 0; - timer_mod(s->frame_timer, t_now + frame_t); + /* expire_time is calculated from last frame_time, we should calculate it + * according to new frame_time which equals to + * NANOSECONDS_PER_SECOND / uhci_frame_timer_freq */ + s->expire_time -= s->frame_time - NANOSECONDS_PER_SECOND / uhci_frame_timer_freq; + s->frame_time = NANOSECONDS_PER_SECOND / uhci_frame_timer_freq; + timer_mod(s->frame_timer, t_now + s->frame_time); } static const MemoryRegionOps uhci_ioport_ops = { @@ -1196,8 +1239,10 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) s->bh = qemu_bh_new(uhci_bh, s); s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; + s->frame_time = NANOSECONDS_PER_SECOND / uhci_frame_timer_freq; QTAILQ_INIT(&s->queues); + qemu_register_usb_controller(&qemu_uhci, QEMU_USB_CONTROLLER_UHCI); memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s, "uhci", 0x20); diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index c85ab7868e..5194d22ab4 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -50,6 +50,7 @@ typedef struct UHCIState { uint16_t status; uint16_t intr; /* interrupt enable register */ uint16_t frnum; /* frame number */ + uint64_t frame_time; /* frame time in ns */ uint32_t fl_base_addr; /* frame list base address */ uint8_t sof_timing; uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index d0d46dd0a4..8f521ad586 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -945,6 +945,30 @@ static void usb_host_ep_update(USBHostDevice *s) libusb_free_config_descriptor(conf); } +static unsigned int usb_get_controller_type(int speed) +{ + unsigned int type = MAX_USB_CONTROLLER_TYPES; + + switch (speed) { + case USB_SPEED_SUPER: + type = QEMU_USB_CONTROLLER_XHCI; + break; + case USB_SPEED_HIGH: + type = QEMU_USB_CONTROLLER_EHCI; + break; + case USB_SPEED_FULL: + type = QEMU_USB_CONTROLLER_UHCI; + break; + case USB_SPEED_LOW: + type = QEMU_USB_CONTROLLER_OHCI; + break; + default: + break; + } + + return type; +} + static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) { USBDevice *udev = USB_DEVICE(s); @@ -1054,6 +1078,12 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) } trace_usb_host_open_success(bus_num, addr); + + /* change ehci frame time freq when USB passthrough */ + qemu_log("usb host speed is %d\n", udev->speed); + qemu_timer_set_mode(QEMU_TIMER_USB_NORMAL_MODE, + usb_get_controller_type(udev->speed)); + return 0; fail: @@ -1129,6 +1159,8 @@ static int usb_host_close(USBHostDevice *s) } usb_host_auto_check(NULL); + qemu_timer_set_mode(QEMU_TIMER_USB_LAZY_MODE, + usb_get_controller_type(udev->speed)); return 0; } diff --git a/include/hw/usb.h b/include/hw/usb.h index 33668dd0a9..fa3a176159 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -142,6 +142,7 @@ #define USB_DEVICE_SELF_POWERED 0 #define USB_DEVICE_REMOTE_WAKEUP 1 +#define USB_DEVICE_REMOTE_WAKEUP_IS_SUPPORTED 2 #define USB_DT_DEVICE 0x01 #define USB_DT_CONFIG 0x02 diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 88ef114689..d263fad9a4 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -91,6 +91,34 @@ struct QEMUTimer { int scale; }; +#define QEMU_USB_NORMAL_FREQ 1000 +#define QEMU_USB_LAZY_FREQ 10 +#define MAX_USB_CONTROLLER_TYPES 4 +#define QEMU_USB_CONTROLLER_OHCI 0 +#define QEMU_USB_CONTROLLER_UHCI 1 +#define QEMU_USB_CONTROLLER_EHCI 2 +#define QEMU_USB_CONTROLLER_XHCI 3 + +typedef void (*QEMUSetFreqHandler) (int freq); + +typedef struct qemu_usb_controller { + const char *name; + QEMUSetFreqHandler qemu_set_freq; +} qemu_usb_controller; + +typedef qemu_usb_controller* qemu_usb_controller_ptr; + +enum qemu_timer_mode { + QEMU_TIMER_USB_NORMAL_MODE = 1 << 0, /* Set when VNC connect or + * with usb dev passthrough + */ + QEMU_TIMER_USB_LAZY_MODE = 1 << 1, /* Set when VNC disconnect */ +}; + +int qemu_register_usb_controller(qemu_usb_controller_ptr controller, + unsigned int type); +int qemu_timer_set_mode(enum qemu_timer_mode mode, unsigned int type); + extern QEMUTimerListGroup main_loop_tlg; /* diff --git a/ui/vnc.c b/ui/vnc.c index af02522e84..bc86c20370 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1379,6 +1379,8 @@ void vnc_disconnect_finish(VncState *vs) g_free(vs->zrle); g_free(vs->tight); g_free(vs); + + qemu_timer_set_mode(QEMU_TIMER_USB_LAZY_MODE, QEMU_USB_CONTROLLER_UHCI); } size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) @@ -3333,6 +3335,8 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, } } } + + qemu_timer_set_mode(QEMU_TIMER_USB_NORMAL_MODE, QEMU_USB_CONTROLLER_UHCI); } void vnc_start_protocol(VncState *vs) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index f36c75e594..40e8c83722 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/lockable.h" @@ -75,6 +76,74 @@ struct QEMUTimerList { QemuEvent timers_done_ev; }; +typedef struct qemu_controller_timer_state { + qemu_usb_controller_ptr controller; + int refs; +} controller_timer_state; + +typedef controller_timer_state* controller_timer_state_ptr; + +static controller_timer_state uhci_timer_state = { + .controller = NULL, + .refs = 0, +}; + +static controller_timer_state_ptr \ + qemu_usb_controller_tab[MAX_USB_CONTROLLER_TYPES] = {NULL, + &uhci_timer_state, + NULL, NULL}; + +int qemu_register_usb_controller(qemu_usb_controller_ptr controller, + unsigned int type) +{ + if (type != QEMU_USB_CONTROLLER_UHCI) { + return 0; + } + + /* for companion EHCI controller will create three UHCI controllers, + * we init it only once. + */ + if (!qemu_usb_controller_tab[type]->controller) { + qemu_log("the usb controller (%d) registed frame handler\n", type); + qemu_usb_controller_tab[type]->controller = controller; + } + + return 0; +} + +int qemu_timer_set_mode(enum qemu_timer_mode mode, unsigned int type) +{ + if (type != QEMU_USB_CONTROLLER_UHCI) { + qemu_log("the usb controller (%d) no need change frame frep\n", type); + return 0; + } + + if (!qemu_usb_controller_tab[type]->controller) { + qemu_log("the usb controller (%d) not registed yet\n", type); + return 0; + } + + if (mode == QEMU_TIMER_USB_NORMAL_MODE) { + if (qemu_usb_controller_tab[type]->refs++ > 0) { + return 0; + } + qemu_usb_controller_tab[type]->controller-> + qemu_set_freq(QEMU_USB_NORMAL_FREQ); + qemu_log("Set the controller (%d) of freq %d HZ,\n", + type, QEMU_USB_NORMAL_FREQ); + } else { + if (--qemu_usb_controller_tab[type]->refs > 0) { + return 0; + } + qemu_usb_controller_tab[type]->controller-> + qemu_set_freq(QEMU_USB_LAZY_FREQ); + qemu_log("Set the controller(type:%d) of freq %d HZ,\n", + type, QEMU_USB_LAZY_FREQ); + } + + return 0; +} + /** * qemu_clock_ptr: * @type: type of clock -- Gitee From 05462305ec8b9ce5b414ede1e7e680b16d1a08ad Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 11 Feb 2022 18:20:59 +0800 Subject: [PATCH 138/486] util/log: add CONFIG_DISABLE_QEMU_LOG macro Using CONFIG_DISABLE_QEMU_LOG macro to control qemu_log function. Signed-off-by: Yan Wang --- util/log.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/log.c b/util/log.c index 2ee1500bee..ed3029fe5c 100644 --- a/util/log.c +++ b/util/log.c @@ -34,6 +34,12 @@ int qemu_loglevel; static int log_append = 0; static GArray *debug_regions; +#ifdef CONFIG_DISABLE_QEMU_LOG +int qemu_log(const char *fmt, ...) +{ + return 0; +} +#else /* Return the number of characters emitted. */ int qemu_log(const char *fmt, ...) { @@ -56,6 +62,7 @@ int qemu_log(const char *fmt, ...) rcu_read_unlock(); return ret; } +#endif static void __attribute__((__constructor__)) qemu_logfile_init(void) { -- Gitee From d0ed3afacd2af1cbfcfb615471ade3c8c4185c00 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 8 Feb 2022 15:48:01 +0800 Subject: [PATCH 139/486] log: Add some logs on VM runtime path Add logs on VM runtime path, to make it easier to do trouble shooting. Signed-off-by: Ying Fang Signed-off-by: Yan Wang --- hw/virtio/virtio-pci.c | 2 ++ hw/virtio/virtio.c | 14 ++++++++++++-- monitor/monitor.c | 9 +++++++++ qapi/qmp-dispatch.c | 15 +++++++++++++++ softmmu/qdev-monitor.c | 4 +++- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 750aa47ec1..38a5dc1ba8 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1772,7 +1772,9 @@ static void virtio_pci_device_unplugged(DeviceState *d) VirtIOPCIProxy *proxy = VIRTIO_PCI(d); bool modern = virtio_pci_modern(proxy); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + qemu_log("unplug device name: %s\n", !vdev ? "NULL" : vdev->name); virtio_pci_stop_ioeventfd(proxy); if (modern) { diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ea7c079fb0..9b4ac58a16 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1945,7 +1945,14 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) k->set_status(vdev, val); } vdev->status = val; - + if (val) { + qemu_log("%s device status is %d that means %s\n", + vdev->name, val, + (val & VIRTIO_CONFIG_S_DRIVER_OK) ? "DRIVER OK" : + (val & VIRTIO_CONFIG_S_DRIVER) ? "DRIVER" : + (val & VIRTIO_CONFIG_S_ACKNOWLEDGE) ? "ACKNOWLEDGE" : + (val & VIRTIO_CONFIG_S_FAILED) ? "FAILED" : "UNKNOWN"); + } return 0; } @@ -2389,8 +2396,11 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, break; } - if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) + if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) { + qemu_log("unacceptable queue_size (%d) or num (%d)\n", + queue_size, i); abort(); + } vdev->vq[i].vring.num = queue_size; vdev->vq[i].vring.num_default = queue_size; diff --git a/monitor/monitor.c b/monitor/monitor.c index 21c7a68758..013c628695 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -29,6 +29,7 @@ #include "qapi/qapi-emit-events.h" #include "qapi/qapi-visit-control.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "sysemu/qtest.h" @@ -318,6 +319,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) { Monitor *mon; MonitorQMP *qmp_mon; + GString *json; trace_monitor_protocol_event_emit(event, qdict); QTAILQ_FOREACH(mon, &mon_list, entry) { @@ -328,6 +330,13 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) qmp_mon = container_of(mon, MonitorQMP, common); if (qmp_mon->commands != &qmp_cap_negotiation_commands) { qmp_send_response(qmp_mon, qdict); + json = qobject_to_json(QOBJECT(qdict)); + if (json) { + if (!strstr(json->str, "RTC_CHANGE")) { + qemu_log("%s\n", json->str); + } + g_string_free(json, true); + } } } } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index d378bccac7..bb005594d3 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -25,6 +25,7 @@ #include "qapi/qmp/qbool.h" #include "qemu/coroutine.h" #include "qemu/main-loop.h" +#include "qemu/log.h" Visitor *qobject_input_visitor_new_qmp(QObject *obj) { @@ -147,6 +148,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, QObject *id; QObject *ret = NULL; QDict *rsp = NULL; + GString *json; dict = qobject_to(QDict, request); if (!dict) { @@ -204,6 +206,19 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, qobject_ref(args); } + json = qobject_to_json(QOBJECT(args)); + if (json) { + if ((strcmp(command, "query-block-jobs") != 0) + && (strcmp(command, "query-migrate") != 0) + && (strcmp(command, "query-blockstats") != 0) + && (strcmp(command, "query-balloon") != 0) + && (strcmp(command, "set_password") != 0)) { + qemu_log("qmp_cmd_name: %s, arguments: %s\n", + command, json->str); + } + g_string_free(json, true); + } + assert(!(oob && qemu_in_coroutine())); assert(monitor_cur() == NULL); if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 01f3834db5..dfd6429bf3 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -36,6 +36,7 @@ #include "qemu/option.h" #include "qemu/qemu-print.h" #include "qemu/option_int.h" +#include "qemu/log.h" #include "sysemu/block-backend.h" #include "migration/misc.h" #include "migration/migration.h" @@ -635,6 +636,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, if (path != NULL) { bus = qbus_find(path, errp); if (!bus) { + error_setg(errp, "can not find bus for %s", driver); return NULL; } if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { @@ -707,7 +709,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, if (*errp) { goto err_del_dev; } - + qemu_log("add qdev %s:%s success\n", driver, dev->id ? dev->id : "none"); if (!qdev_realize(DEVICE(dev), bus, errp)) { goto err_del_dev; } -- Gitee From ada323e932c83271184a6ddba1cfd74a29378963 Mon Sep 17 00:00:00 2001 From: Kunkun Jiang Date: Thu, 29 Jul 2021 15:24:48 +0800 Subject: [PATCH 140/486] qdev/monitors: Fix reundant error_setg of qdev_add_device There is an extra log "error_setg" in qdev_add_device(). When hot-plug a device, if the corresponding bus doesn't exist, it will trigger an asseration "assert(*errp == NULL)". Fixes: 515a7970490 (log: Add some logs on VM runtime path) Signed-off-by: Kunkun Jiang Signed-off-by: Yan Wang --- softmmu/qdev-monitor.c | 1 - 1 file changed, 1 deletion(-) diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index dfd6429bf3..4a20f5dbd7 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -636,7 +636,6 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, if (path != NULL) { bus = qbus_find(path, errp); if (!bus) { - error_setg(errp, "can not find bus for %s", driver); return NULL; } if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { -- Gitee From 00c4115a1388ee72295b99fce1f6ad49bf761134 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 10 Feb 2022 17:08:08 +0800 Subject: [PATCH 141/486] bios-tables-test: Allow changes to q35/SSDT.dimmpxm file List test/data/acpi/q35/SSDT.dimmpxm as the expected files allowed to be changed in tests/qtest/bios-tables-test-allowed-diff.h Signed-off-by: Yan Wang --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..81148a604f 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/q35/SSDT.dimmpxm", -- Gitee From 937e22eda2480a64095928ee8df0d37b3313bb64 Mon Sep 17 00:00:00 2001 From: Ying Fang Date: Tue, 14 Apr 2020 14:53:44 +0800 Subject: [PATCH 142/486] smbios: Add missing member of type 4 for smbios 3.0 According to smbios 3.0 spec, for processor information (type 4), it adds three new members (Core Count 2, Core enabled 2, thread count 2) for 3.0, Without this three members, we can not get correct cpu frequency from dmi, Because it will failed to check the length of Processor Infomation in DMI. The corresponding codes in kernel is like: if (dm->type == DMI_ENTRY_PROCESSOR && dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) { u16 val = (u16)get_unaligned((const u16 *) (dmi_data + DMI_PROCESSOR_MAX_SPEED)); *mhz = val > *mhz ? val : *mhz; } Signed-off-by: zhanghailiang Signed-off-by: Yan Wang --- hw/smbios/smbios.c | 4 +++- include/hw/firmware/smbios.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 7397e56737..66be9aee09 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -688,7 +688,9 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance) t->thread_count = ms->smp.threads; t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ t->processor_family2 = cpu_to_le16(0x01); /* Other */ - + t->corecount2 = 0; + t->enabledcorecount2 = 0; + t->threadcount2 = 0; SMBIOS_BUILD_TABLE_POST; smbios_type4_count++; } diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 5a0dd0c8cf..5a696cf75a 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -193,6 +193,9 @@ struct smbios_type_4 { uint8_t thread_count; uint16_t processor_characteristics; uint16_t processor_family2; + uint16_t corecount2; + uint16_t enabledcorecount2; + uint16_t threadcount2; } QEMU_PACKED; /* SMBIOS type 11 - OEM strings */ -- Gitee From 8940f11a055da0a744d10b53cf999dea7967be25 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 10 Feb 2022 17:12:35 +0800 Subject: [PATCH 143/486] bios-tables-test: Update expected q35/SSDT.dimmpxm file Run ./tests/data/acpi/rebuild-expected-aml.sh from build directory to update q35/SSDT.dimmpxm file. Also empty bios-tables-test-allowed-diff.h. The disassembled differences between actual and expected SSDT.dimmpxm: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20210604 (64-bit version) * Copyright (c) 2000 - 2021 Intel Corporation * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/q35/SSDT.dimmpxm, Thu Feb 10 15:03:52 2022 + * Disassembly of /tmp/aml-CK68G1, Thu Feb 10 15:03:52 2022 * * Original Table Header: * Signature "SSDT" * Length 0x000002DE (734) * Revision 0x01 - * Checksum 0x06 + * Checksum 0x16 * OEM ID "BOCHS " * OEM Table ID "NVDIMM " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "SSDT", 1, "BOCHS ", "NVDIMM ", 0x00000001) { Scope (\_SB) { Device (NVDR) { Name (_HID, "ACPI0012" /* NVDIMM Root Device */) // _HID: Hardware ID Method (NCAL, 5, Serialized) { Local6 = MEMA /* \MEMA */ @@ -187,19 +187,19 @@ { Return (NCAL (Arg0, Arg1, Arg2, Arg3, 0x02)) } } Device (NV02) { Name (_ADR, 0x03) // _ADR: Address Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { Return (NCAL (Arg0, Arg1, Arg2, Arg3, 0x03)) } } } } - Name (MEMA, 0x07FFF000) + Name (MEMA, 0x07FFE000) } Signed-off-by: Yan Wang --- tests/data/acpi/q35/SSDT.dimmpxm | Bin 734 -> 734 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm index 617a1c911c7d6753bcedc8ecc52e3027a5259ad6..a50a961fa1d9b0dd8ea4096d652c83bcf04db20b 100644 GIT binary patch delta 23 fcmcb|dXJSWIM^lR9uortqu55Si%iT9{<8xBSkVW4 delta 23 fcmcb|dXJSWIM^lR9uortBilx Date: Sat, 18 Dec 2021 09:39:57 +0800 Subject: [PATCH 144/486] net: eepro100: validate various address valuesi(CVE-2021-20255) fix CVE-2021-20255 patch link: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg06098.html fix CVE-2021-20255, sync patch from ostms platform. Signed-off-by: zhouli57 Signed-off-by: Yan Wang --- hw/net/eepro100.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 16e95ef9cc..2474cf3dc2 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -279,6 +279,9 @@ typedef struct { /* Quasi static device properties (no need to save them). */ uint16_t stats_size; bool has_extended_tcb_support; + + /* Flag to avoid recursions. */ + bool busy; } EEPRO100State; /* Word indices in EEPROM. */ @@ -837,6 +840,14 @@ static void action_command(EEPRO100State *s) Therefore we limit the number of iterations. */ unsigned max_loop_count = 16; + if (s->busy) { + /* Prevent recursions. */ + logout("recursion in %s:%u\n", __FILE__, __LINE__); + return; + } + + s->busy = true; + for (;;) { bool bit_el; bool bit_s; @@ -933,6 +944,7 @@ static void action_command(EEPRO100State *s) } TRACE(OTHER, logout("CU list empty\n")); /* List is empty. Now CU is idle or suspended. */ + s->busy = false; } static void eepro100_cu_command(EEPRO100State * s, uint8_t val) -- Gitee From 92da19fb18c234bb8872b9d8f7dedcc73e5fcafb Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Wed, 14 Oct 2020 15:00:20 +0800 Subject: [PATCH 145/486] pci: check bus pointer before dereference fix CVE-2020-25742 patch link: https://lists.nongnu.org/archive/html/qemu-devel/2020-09/msg05294.html While mapping IRQ level in pci_change_irq_level() routine, it does not check if pci_get_bus() returned a valid pointer. It may lead to a NULL pointer dereference issue. Add check to avoid it. -> https://ruhr-uni-bochum.sciebo.de/s/NNWP2GfwzYKeKwE?path=%2Flsi_nullptr1 ==1183858==Hint: address points to the zero page. #0 pci_change_irq_level hw/pci/pci.c:259 #1 pci_irq_handler hw/pci/pci.c:1445 #2 pci_set_irq hw/pci/pci.c:1463 #3 lsi_set_irq hw/scsi/lsi53c895a.c:488 #4 lsi_update_irq hw/scsi/lsi53c895a.c:523 #5 lsi_script_scsi_interrupt hw/scsi/lsi53c895a.c:554 #6 lsi_execute_script hw/scsi/lsi53c895a.c:1149 #7 lsi_reg_writeb hw/scsi/lsi53c895a.c:1984 #8 lsi_io_write hw/scsi/lsi53c895a.c:2146 ... Reported-by: Ruhr-University Signed-off-by: Prasad J Pandit Signed-off-by: Yan Wang --- hw/pci/pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e5993c1ef5..6d1c39a9de 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -270,6 +270,9 @@ static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) PCIBus *bus; for (;;) { bus = pci_get_bus(pci_dev); + if (!bus) { + return; + } irq_num = bus->map_irq(pci_dev, irq_num); if (bus->set_irq) break; -- Gitee From 9169beed83ea77059a7240aae5621dcfb3178cba Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Mon, 21 Jun 2021 09:22:35 +0800 Subject: [PATCH 146/486] ide: ahci: add check to avoid null dereference (CVE-2019-12067) Fix CVE-2019-12067 AHCI emulator while committing DMA buffer in ahci_commit_buf() may do a NULL dereference if the command header 'ad->cur_cmd' is null. Add check to avoid it. Reported-by: Bugs SysSec Signed-off-by: Prasad J Pandit Signed-off-by: Jiajie Li Signed-off-by: Yan Wang --- hw/ide/ahci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index a94c6e26fb..256b58026a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1459,8 +1459,10 @@ static void ahci_commit_buf(const IDEDMA *dma, uint32_t tx_bytes) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - tx_bytes += le32_to_cpu(ad->cur_cmd->status); - ad->cur_cmd->status = cpu_to_le32(tx_bytes); + if (ad->cur_cmd) { + tx_bytes += le32_to_cpu(ad->cur_cmd->status); + ad->cur_cmd->status = cpu_to_le32(tx_bytes); + } } static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write) -- Gitee From 48a38f409a25f26605d65346c8ed9403c4b36c80 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 10 Feb 2022 10:28:59 +0800 Subject: [PATCH 147/486] tap: return err when tap TUNGETIFF fail When hotplug ovs kernel netcard, even tap TUNGETIFF failed, the hotplug would go on and would lead to qemu assert. The failure should lead to the free_fail. Signed-off-by: miaoyubo Signed-off-by: Yan Wang --- net/tap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tap.c b/net/tap.c index f716be3e3f..c5cbeaa7a2 100644 --- a/net/tap.c +++ b/net/tap.c @@ -900,6 +900,7 @@ int net_init_tap(const Netdev *netdev, const char *name, if (i == 0) { vnet_hdr = tap_probe_vnet_hdr(fd, errp); if (vnet_hdr < 0) { + ret = -1; goto free_fail; } } else if (vnet_hdr != tap_probe_vnet_hdr(fd, NULL)) { -- Gitee From a95ada20170af0a71529c1583846e402cdbb850b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 10 Feb 2022 10:41:40 +0800 Subject: [PATCH 148/486] xhci: check reg to avoid OOB read Add a sanity check to fix OOB read access. Signed-off-by: Yan Wang --- hw/usb/hcd-xhci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index e01700039b..08cd63e159 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -27,6 +27,7 @@ #include "hw/qdev-properties.h" #include "trace.h" #include "qapi/error.h" +#include "qemu/log.h" #include "hcd-xhci.h" @@ -3017,14 +3018,17 @@ static void xhci_runtime_write(void *ptr, hwaddr reg, XHCIInterrupter *intr; int v; - trace_usb_xhci_runtime_write(reg, val); - if (reg < 0x20) { trace_usb_xhci_unimplemented("runtime write", reg); return; } v = (reg - 0x20) / 0x20; + if (v >= xhci->numintrs) { + qemu_log("intr nr out of range (%d >= %d)\n", v, xhci->numintrs); + return; + } intr = &xhci->intr[v]; + trace_usb_xhci_runtime_write(reg, val); switch (reg & 0x1f) { case 0x00: /* IMAN */ -- Gitee From f5af9ac3c9af4602812060759f6f95da8725314b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 10 Feb 2022 11:18:13 +0800 Subject: [PATCH 149/486] monitor: Discard BLOCK_IO_ERROR event when VM rebooted Throttled event like QAPI_EVENT_BLOCK_IO_ERROR may be queued to limit event rate. Event may be delivered when VM is rebooted if the event was queued in the *monitor_qapi_event_state* hash table. Which may casue VM pause and other related problems. Such as seabios blocked during virtio-scsi initialization: vring_add_buf(vq, sg, out_num, in_num, 0, 0); vring_kick(vp, vq, 1); ------------> VM paused here <----------- /* Wait for reply */ while (!vring_more_used(vq)) usleep(5); Signed-off-by: Yan Wang --- include/monitor/monitor.h | 2 ++ monitor/monitor.c | 30 ++++++++++++++++++++++++++++++ softmmu/runstate.c | 1 + 3 files changed, 33 insertions(+) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 12d395d62d..847445f972 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -56,4 +56,6 @@ void monitor_register_hmp(const char *name, bool info, void monitor_register_hmp_info_hrt(const char *name, HumanReadableText *(*handler)(Error **errp)); +void monitor_qapi_event_discard_io_error(void); + #endif /* MONITOR_H */ diff --git a/monitor/monitor.c b/monitor/monitor.c index 013c628695..fb4ae9531c 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -34,6 +34,9 @@ #include "qemu/option.h" #include "sysemu/qtest.h" #include "trace.h" +#include "qemu/log.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qobject.h" /* * To prevent flooding clients, events can be throttled. The @@ -767,6 +770,33 @@ int monitor_init_opts(QemuOpts *opts, Error **errp) return ret; } +void monitor_qapi_event_discard_io_error(void) +{ + GHashTableIter event_iter; + MonitorQAPIEventState *evstate; + gpointer key, value; + GString *json; + + qemu_mutex_lock(&monitor_lock); + g_hash_table_iter_init(&event_iter, monitor_qapi_event_state); + while (g_hash_table_iter_next(&event_iter, &key, &value)) { + evstate = key; + /* Only QAPI_EVENT_BLOCK_IO_ERROR is discarded */ + if (evstate->event == QAPI_EVENT_BLOCK_IO_ERROR) { + g_hash_table_iter_remove(&event_iter); + json = qobject_to_json(QOBJECT(evstate->qdict)); + qemu_log(" %s event discarded\n", json->str); + timer_del(evstate->timer); + timer_free(evstate->timer); + qobject_unref(evstate->data); + qobject_unref(evstate->qdict); + g_string_free(json, true); + g_free(evstate); + } + } + qemu_mutex_unlock(&monitor_lock); +} + QemuOptsList qemu_mon_opts = { .name = "mon", .implied_opt_name = "chardev", diff --git a/softmmu/runstate.c b/softmmu/runstate.c index 10d9b7365a..5736d908db 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -448,6 +448,7 @@ void qemu_system_reset(ShutdownCause reason) qapi_event_send_reset(shutdown_caused_by_guest(reason), reason); } cpu_synchronize_all_post_reset(); + monitor_qapi_event_discard_io_error(); } /* -- Gitee From 44f45b5c163efed5387dac40e229e0a50bf5921a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 10 Feb 2022 11:35:58 +0800 Subject: [PATCH 150/486] monitor: limit io error qmp event to at most once per 60s The speed of BLOCK IO ERROR event maybe very high (thousands per second). If we report all BLOCK IO ERRORs, the log file will be flooded with BLOCK IO ERROR event. So throttle it to at most once per 60s. Signed-off-by: Yan Wang --- monitor/monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/monitor/monitor.c b/monitor/monitor.c index fb4ae9531c..621e79eb66 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -300,6 +300,7 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS }, [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, [QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_BLOCK_IO_ERROR] = { 60L * 1000 * SCALE_MS }, }; /* -- Gitee From 0a0a490c805fadc7191489277e77fbf9688b39ab Mon Sep 17 00:00:00 2001 From: jiangdongxu Date: Thu, 10 Feb 2022 21:32:37 +0800 Subject: [PATCH 151/486] bugfix: fix some illegal memory access and memory leak Signed-off-by: yuxiating Signed-off-by: jiangdongxu --- contrib/elf2dmp/main.c | 1 + hw/display/cirrus_vga.c | 2 +- util/range.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index 20b477d582..3f0d1eb709 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -125,6 +125,7 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb, if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) { eprintf("Failed to extract entire KDBG\n"); + free(kdbg); return NULL; } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index fdca6ca659..c66ed801ef 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -834,7 +834,7 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s) word alignment, so we keep them for the next line */ /* XXX: keep alignment to speed up transfer */ end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; - copy_count = s->cirrus_srcptr_end - end_ptr; + copy_count = MIN(s->cirrus_srcptr_end - end_ptr, CIRRUS_BLTBUFSIZE); memmove(s->cirrus_bltbuf, end_ptr, copy_count); s->cirrus_srcptr = s->cirrus_bltbuf + copy_count; s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; diff --git a/util/range.c b/util/range.c index 098d9d2dc0..83d1a6c302 100644 --- a/util/range.c +++ b/util/range.c @@ -65,6 +65,7 @@ GList *range_list_insert(GList *list, Range *data) range_extend(l->data, l->next->data); g_free(l->next->data); new_l = g_list_delete_link(list, l->next); + l->next = NULL; assert(new_l == list); } -- Gitee From 03e7e232a323c45205d3c6ecb7d8e52e7209d9eb Mon Sep 17 00:00:00 2001 From: jiangdongxu Date: Thu, 10 Feb 2022 22:12:50 +0800 Subject: [PATCH 152/486] bugfix: fix possible memory leak Signed-off-by: caojinhua Signed-off-by: jiangdongxu --- migration/savevm.c | 2 ++ qga/main.c | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index d59e976d50..803cd9004d 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1427,6 +1427,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, ret = vmstate_save(f, se, vmdesc); if (ret) { qemu_file_set_error(f, ret); + json_writer_free(vmdesc); return ret; } trace_savevm_section_end(se->idstr, se->section_id, 0); @@ -1443,6 +1444,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, error_report("%s: bdrv_inactivate_all() failed (%d)", __func__, ret); qemu_file_set_error(f, ret); + json_writer_free(vmdesc); return ret; } } diff --git a/qga/main.c b/qga/main.c index 15fd3a4149..6f09a689ac 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1283,7 +1283,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) if (g_mkdir_with_parents(config->state_dir, S_IRWXU) == -1) { g_critical("unable to create (an ancestor of) the state directory" " '%s': %s", config->state_dir, strerror(errno)); - return NULL; + goto failed; } #endif @@ -1308,7 +1308,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) if (!log_file) { g_critical("unable to open specified log file: %s", strerror(errno)); - return NULL; + goto failed; } s->log_file = log_file; } @@ -1319,7 +1319,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->pstate_filepath, ga_is_frozen(s))) { g_critical("failed to load persistent state"); - return NULL; + goto failed; } config->blacklist = ga_command_blacklist_init(config->blacklist); @@ -1340,7 +1340,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) #ifndef _WIN32 if (!register_signal_handlers()) { g_critical("failed to register signal handlers"); - return NULL; + goto failed; } #endif @@ -1353,12 +1353,20 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->wakeup_event = CreateEvent(NULL, TRUE, FALSE, TEXT("WakeUp")); if (s->wakeup_event == NULL) { g_critical("CreateEvent failed"); - return NULL; + goto failed; } #endif ga_state = s; return s; +failed: + g_free(s->pstate_filepath); + g_free(s->state_filepath_isfrozen); + if (s->log_file) { + fclose(s->log_file); + } + g_free(s); + return NULL; } static void cleanup_agent(GAState *s) -- Gitee From 02a17066ac3dfb5e53b72b15a80643154990191b Mon Sep 17 00:00:00 2001 From: jiangdongxu Date: Thu, 10 Feb 2022 21:50:28 +0800 Subject: [PATCH 153/486] bugfix: fix eventfds may double free when vm_id reused in ivshmem As the ivshmem Server-Client Protol describes, when a client disconnects from the server, server sends disconnect notifications to the other clients. And the other clients will free the eventfds of the disconnected client according to the client ID. If the client ID is reused, the eventfds may be double freed. It will be solved by setting eventfds to NULL after freeing and allocating memory for it when it's used. Signed-off-by: Peng Liang Signed-off-by: jiangdongxu --- hw/misc/ivshmem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 1ba4a98377..05f06ed6cf 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -400,6 +400,7 @@ static void close_peer_eventfds(IVShmemState *s, int posn) } g_free(s->peers[posn].eventfds); + s->peers[posn].eventfds = NULL; s->peers[posn].nb_eventfds = 0; } @@ -530,6 +531,10 @@ static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, close(fd); return; } + if (peer->eventfds == NULL) { + peer->eventfds = g_new0(EventNotifier, s->vectors); + peer->nb_eventfds = 0; + } vector = peer->nb_eventfds++; IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); -- Gitee From 7448eb87ee59856aa0f0853f2aa5b803c832fccf Mon Sep 17 00:00:00 2001 From: jiangdongxu Date: Thu, 10 Feb 2022 21:37:49 +0800 Subject: [PATCH 154/486] block/mirror: fix file-system went to read-only after block-mirror config vm disk with prdm, keep the disk writing data continuously during block-mirror, the file-system will went to read-only after block-mirror, fix it. Signed-off-by: caojinhua Signed-off-by: jiangdongxu --- block/mirror.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/mirror.c b/block/mirror.c index efec2c7674..b7f0cba9b9 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1640,7 +1640,7 @@ static BlockJob *mirror_start_job( * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, filter_node_name, - BDRV_O_RDWR, errp); + BDRV_O_RDWR | BDRV_O_NOCACHE, errp); if (mirror_top_bs == NULL) { return NULL; } -- Gitee From f14ea0bd2596f94ad926009411b8ffda9c2c2cda Mon Sep 17 00:00:00 2001 From: jiangdongxu Date: Thu, 10 Feb 2022 22:42:23 +0800 Subject: [PATCH 155/486] bugfix: fix mmio information leak and ehci vm escape 0-day vulnerability Signed-off-by: Yutao Ai Signed-off-by: jiangdongxu --- hw/usb/core.c | 20 ++++++++++++++++++-- hw/usb/hcd-ehci.c | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/hw/usb/core.c b/hw/usb/core.c index 51b36126ca..a62826e051 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -206,7 +206,15 @@ static void do_token_in(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (s->setup_buf[0] & USB_DIR_IN) { - int len = s->setup_len - s->setup_index; + int len; + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small do_token_in(%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } + len = s->setup_len - s->setup_index; if (len > p->iov.size) { len = p->iov.size; } @@ -244,7 +252,15 @@ static void do_token_out(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (!(s->setup_buf[0] & USB_DIR_IN)) { - int len = s->setup_len - s->setup_index; + int len; + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small do_token_out(%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } + len = s->setup_len - s->setup_index; if (len > p->iov.size) { len = p->iov.size; } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 6caa7ac6c2..1415107315 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -612,6 +612,8 @@ static void ehci_free_queue(EHCIQueue *q, const char *warn) ehci_trace_guest_bug(q->ehci, warn); } QTAILQ_REMOVE(head, q, next); + memset(q, 0, sizeof(*q)); + *(volatile char *)q = *(volatile char *)q; g_free(q); } -- Gitee From 6e070be26502e171fd5d43a128dea99f1d34429b Mon Sep 17 00:00:00 2001 From: jiangdongxu Date: Thu, 10 Feb 2022 21:41:06 +0800 Subject: [PATCH 156/486] target-i386: Fix the RES memory inc which caused by the coroutine created for better performance, change the POOL_BATCH_SIZE from 64 to 128. Signed-off-by: caojinhua Signed-off-by: jiangdongxu --- util/qemu-coroutine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 38fb6d3084..b9586d6929 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -21,7 +21,7 @@ #include "block/aio.h" enum { - POOL_BATCH_SIZE = 64, + POOL_BATCH_SIZE = 128, }; /** Free list to speed up creation */ -- Gitee From 32353a7838f9ff38c5bd768252a79bd8e485658b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 27 Jul 2020 20:39:07 +0800 Subject: [PATCH 157/486] bugfix: irq: Avoid covering object refcount of qemu_irq Avoid covering object refcount of qemu_irq, otherwise it may causes memory leak. Signed-off-by: Keqian Zhu --- hw/core/irq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/core/irq.c b/hw/core/irq.c index 8a9cbdd556..700a6373d8 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -126,7 +126,10 @@ void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n) int i; qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n); for (i = 0; i < n; i++) { - *old_irqs[i] = *gpio_in[i]; + old_irqs[i]->handler = gpio_in[i]->handler; + old_irqs[i]->opaque = gpio_in[i]->opaque; + old_irqs[i]->n = gpio_in[i]->n; + gpio_in[i]->handler = handler; gpio_in[i]->opaque = &old_irqs[i]; } -- Gitee From 2d3abbcc8f6c6db582d931ba194b26dd7148849f Mon Sep 17 00:00:00 2001 From: "wanghaibin.wang" Date: Mon, 16 Oct 2017 18:01:59 +0800 Subject: [PATCH 158/486] log: Add log at boot & cpu init for aarch64 Add log at boot & cpu init for aarch64 Signed-off-by: miaoyubo Signed-off-by: Jingyi Wang --- hw/arm/boot.c | 4 ++++ hw/arm/virt.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 74ad397b1f..21024f7999 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -12,6 +12,7 @@ #include "qemu/datadir.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qemu/log.h" #include #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" @@ -1317,6 +1318,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) * doesn't support secure. */ assert(!(info->secure_board_setup && kvm_enabled())); + + qemu_log("load the kernel\n"); + info->kernel_filename = ms->kernel_filename; info->kernel_cmdline = ms->kernel_cmdline; info->initrd_filename = ms->initrd_filename; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0538d258fa..47e98f09e8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -33,6 +33,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/option.h" +#include "qemu/log.h" #include "monitor/qdev.h" #include "qapi/error.h" #include "hw/sysbus.h" @@ -971,6 +972,7 @@ static void virt_powerdown_req(Notifier *n, void *opaque) { VirtMachineState *s = container_of(n, VirtMachineState, powerdown_notifier); + qemu_log("send powerdown to vm.\n"); if (s->acpi_dev) { acpi_send_event(s->acpi_dev, ACPI_POWER_DOWN_STATUS); } else { @@ -2072,6 +2074,7 @@ static void machvirt_init(MachineState *machine) } create_fdt(vms); + qemu_log("cpu init start\n"); possible_cpus = mc->possible_cpu_arch_ids(machine); assert(possible_cpus->len == max_cpus); -- Gitee From 1a0b974a0aaff667a76972403c28c66416c2947b Mon Sep 17 00:00:00 2001 From: "wangxinxin.wang@huawei.com" Date: Tue, 27 Jun 2017 17:42:23 +0800 Subject: [PATCH 159/486] feature: Add log for each modules add log for each modules. Signed-off-by: miaoyubo Signed-off-by: Jingyi Wang --- accel/kvm/kvm-all.c | 5 ++++- hw/char/virtio-serial-bus.c | 5 +++++ hw/pci/pci.c | 1 + hw/usb/bus.c | 6 ++++++ hw/usb/host-libusb.c | 5 +++++ hw/virtio/virtio-scsi-pci.c | 3 +++ monitor/monitor.c | 1 + monitor/qmp-cmds.c | 3 +++ os-posix.c | 1 + qapi/qmp-dispatch.c | 15 +++++++++++++++ softmmu/qdev-monitor.c | 5 +++++ 11 files changed, 49 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index b128d311c2..8a98446b7c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1751,7 +1751,10 @@ void kvm_irqchip_commit_routes(KVMState *s) s->irq_routes->flags = 0; trace_kvm_irqchip_commit_routes(); ret = kvm_vm_ioctl(s, KVM_SET_GSI_ROUTING, s->irq_routes); - assert(ret == 0); + if (ret < 0) { + error_report("Set GSI routing failed: %m"); + abort(); + } } static void kvm_add_routing_entry(KVMState *s, diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index f01ec2137c..edb7a44ee9 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -257,6 +257,8 @@ static size_t send_control_event(VirtIOSerial *vser, uint32_t port_id, virtio_stw_p(vdev, &cpkt.value, value); trace_virtio_serial_send_control_event(port_id, event, value); + qemu_log("virtio serial port %d send control message" + " event = %d, value = %d\n", port_id, event, value); return send_control_msg(vser, &cpkt, sizeof(cpkt)); } @@ -364,6 +366,9 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); + qemu_log("virtio serial port '%u' handle control message" + " event = %d, value = %d\n", + virtio_ldl_p(vdev, &gcpkt->id), cpkt.event, cpkt.value); if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { if (!cpkt.value) { diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 850735fc46..0743dc7c42 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2411,6 +2411,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, } else { snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev))); } + qemu_log("add rom file: %s\n", name); pdev->has_rom = true; memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, pdev->romsize, &error_fatal); ptr = memory_region_get_ram_ptr(&pdev->rom); diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 92d6ed5626..20cd9b6e6f 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -536,6 +536,10 @@ void usb_check_attach(USBDevice *dev, Error **errp) bus->qbus.name, port->path, portspeed); return; } + + qemu_log("attach usb device \"%s\" (%s speed) to VM bus \"%s\", " + "port \"%s\" (%s speed)\n", dev->product_desc, devspeed, + bus->qbus.name, port->path, portspeed); } void usb_device_attach(USBDevice *dev, Error **errp) @@ -564,6 +568,8 @@ int usb_device_detach(USBDevice *dev) usb_detach(port); dev->attached = false; + qemu_log("detach usb device \"%s\" from VM bus \"%s\", port \"%s\"\n", + dev->product_desc, bus->qbus.name, port->path); return 0; } diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 8f521ad586..3394b04f50 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -992,6 +992,8 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) rc = libusb_open(dev, &s->dh); if (rc != 0) { + qemu_log("libusb open usb device bus %d, device %d failed\n", + bus_num, addr); goto fail; } } else { @@ -1019,6 +1021,7 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) libusb_get_device_descriptor(dev, &s->ddesc); usb_host_get_port(s->dev, s->port, sizeof(s->port)); + qemu_log("open a host usb device on bus %d, device %d\n", bus_num, addr); usb_ep_init(udev); usb_host_ep_update(s); @@ -1146,6 +1149,8 @@ static int usb_host_close(USBHostDevice *s) usb_device_detach(udev); } + qemu_log("begin to reset the usb device, bus : %d, device : %d\n", + s->bus_num, s->addr); usb_host_release_interfaces(s); libusb_reset_device(s->dh); usb_host_attach_kernel(s); diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index 97fab74236..498f9e2c98 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -18,6 +18,7 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/module.h" +#include "qemu/log.h" #include "virtio-pci.h" #include "qom/object.h" @@ -51,6 +52,8 @@ static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOSCSIConf *conf = &dev->vdev.parent_obj.conf; char *bus_name; + qemu_log("virtio scsi HBA %s begin to initialize.\n", + !proxy->id ? "NULL" : proxy->id); if (conf->num_queues == VIRTIO_SCSI_AUTO_NUM_QUEUES) { conf->num_queues = virtio_pci_optimal_num_queues(VIRTIO_SCSI_VQ_NUM_FIXED); diff --git a/monitor/monitor.c b/monitor/monitor.c index 621e79eb66..28206bedc4 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "monitor-internal.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 98868cee03..d71beace6a 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -21,6 +21,7 @@ #include "sysemu/sysemu.h" #include "qemu/config-file.h" #include "qemu/uuid.h" +#include "qemu/log.h" #include "chardev/char.h" #include "ui/qemu-spice.h" #include "ui/console.h" @@ -150,8 +151,10 @@ void qmp_cont(Error **errp) } if (runstate_check(RUN_STATE_INMIGRATE)) { + qemu_log("qmp cont is received in migration\n"); autostart = 1; } else { + qemu_log("qmp cont is received and vm is started\n"); vm_start(); } } diff --git a/os-posix.c b/os-posix.c index ae6c9f2a5e..306c442bc8 100644 --- a/os-posix.c +++ b/os-posix.c @@ -322,6 +322,7 @@ int os_mlock(void) #ifdef HAVE_MLOCKALL int ret = 0; + qemu_log("do mlockall\n"); ret = mlockall(MCL_CURRENT | MCL_FUTURE); if (ret < 0) { error_report("mlockall: %s", strerror(errno)); diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index bb005594d3..392ddb097c 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -26,6 +26,7 @@ #include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "qapi/qmp/qstring.h" Visitor *qobject_input_visitor_new_qmp(QObject *obj) { @@ -221,6 +222,20 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, assert(!(oob && qemu_in_coroutine())); assert(monitor_cur() == NULL); + + json = qobject_to_json(QOBJECT(args)); + if (json) { + if ((strcmp(command, "query-block-jobs") != 0) + && (strcmp(command, "query-migrate") != 0) + && (strcmp(command, "query-blockstats") != 0) + && (strcmp(command, "query-balloon") != 0) + && (strcmp(command, "set_password") != 0)) { + qemu_log("qmp_cmd_name: %s, arguments: %s\n", + command, json->str); + } + g_string_free(json, true); + } + if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { monitor_set_cur(qemu_coroutine_self(), cur_mon); cmd->fn(args, &ret, &err); diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 4a20f5dbd7..05e1d88d99 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -636,6 +636,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, if (path != NULL) { bus = qbus_find(path, errp); if (!bus) { + qemu_log("can not find bus for %s\n", driver); return NULL; } if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { @@ -706,6 +707,8 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, errp); if (*errp) { + qemu_log("the bus %s -driver %s set property failed\n", + bus ? bus->name : "None", driver); goto err_del_dev; } qemu_log("add qdev %s:%s success\n", driver, dev->id ? dev->id : "none"); @@ -730,6 +733,8 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) ret = qdev_device_add_from_qdict(qdict, false, errp); if (ret) { + qemu_log("add qdev %s:%s success\n", qemu_opt_get(opts, "driver"), + qemu_opts_id(opts) ? qemu_opts_id(opts) : "none"); qemu_opts_del(opts); } qobject_unref(qdict); -- Gitee From afbf800fa1f5e104a5edf116db4956289990ebe1 Mon Sep 17 00:00:00 2001 From: "wangxinxin.wang@huawei.com" Date: Thu, 22 Jun 2017 08:30:04 +0800 Subject: [PATCH 160/486] feature: Add logs for vm start and destroy Add QEMU_LOG for vm start and destroy Signed-off-by: miaoyubo Signed-off-by: Jingyi Wang --- hw/acpi/core.c | 4 ++++ hw/core/reset.c | 2 ++ softmmu/main.c | 2 ++ softmmu/runstate.c | 2 ++ softmmu/vl.c | 6 ++++++ 5 files changed, 16 insertions(+) diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 1e004d0078..eb631caa91 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -24,6 +24,7 @@ #include "hw/acpi/acpi.h" #include "hw/nvram/fw_cfg.h" #include "qemu/config-file.h" +#include "qemu/log.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-events-run-state.h" @@ -560,13 +561,16 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) uint16_t sus_typ = (val >> 10) & 7; switch (sus_typ) { case 0: /* soft power off */ + qemu_log("VM will be soft power off\n"); qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); break; case 1: + qemu_log("VM will be suspend state\n"); qemu_system_suspend_request(); break; default: if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */ + qemu_log("VM will be S4 state\n"); qapi_event_send_suspend_disk(); qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } diff --git a/hw/core/reset.c b/hw/core/reset.c index 9c477f2bf5..e923723d38 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/queue.h" +#include "qemu/log.h" #include "sysemu/reset.h" /* reset/shutdown handler */ @@ -64,6 +65,7 @@ void qemu_devices_reset(void) { QEMUResetEntry *re, *nre; + qemu_log("reset all devices\n"); /* reset all devices */ QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { re->func(re->opaque); diff --git a/softmmu/main.c b/softmmu/main.c index 639c67ff48..0acb41bd30 100644 --- a/softmmu/main.c +++ b/softmmu/main.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu-common.h" #include "sysemu/sysemu.h" @@ -47,6 +48,7 @@ int main(int argc, char **argv) int main(int argc, char **argv, char **envp) { qemu_init(argc, argv, envp); + qemu_log("qemu enter main_loop\n"); qemu_main_loop(); qemu_cleanup(); diff --git a/softmmu/runstate.c b/softmmu/runstate.c index 5736d908db..52fc3b7d6f 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -708,9 +708,11 @@ static bool main_loop_should_exit(void) } if (qemu_powerdown_requested()) { qemu_system_powerdown(); + qemu_log("domain is power down by outside operation\n"); } if (qemu_vmstop_requested(&r)) { vm_stop(r); + qemu_log("domain is stopped by outside operation\n"); } return false; } diff --git a/softmmu/vl.c b/softmmu/vl.c index d9e4c619d3..d8996f3d6e 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -26,6 +26,7 @@ #include "qemu-common.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "qemu/log.h" #include "exec/cpu-common.h" #include "hw/qdev-properties.h" #include "qapi/compat-policy.h" @@ -2680,6 +2681,7 @@ static void qemu_create_cli_devices(void) } /* init generic devices */ + qemu_log("device init start\n"); rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE); qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, &error_fatal); @@ -2819,6 +2821,7 @@ void qemu_init(int argc, char **argv, char **envp) qemu_init_subsystems(); + qemu_log("qemu pid is %d, options parsing start\n", getpid()); /* first pass of option parsing */ optind = 1; while (optind < argc) { @@ -3027,6 +3030,7 @@ void qemu_init(int argc, char **argv, char **envp) exit(0); break; case QEMU_OPTION_m: + qemu_log("memory options parse start\n"); opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), optarg, true); if (!opts) { @@ -3744,6 +3748,7 @@ void qemu_init(int argc, char **argv, char **envp) */ machine_class = MACHINE_GET_CLASS(current_machine); + qemu_log("configure accelerator %s start\n", machine_class->name); if (!qtest_enabled() && machine_class->deprecation_reason) { error_report("Machine type '%s' is deprecated: %s", machine_class->name, machine_class->deprecation_reason); @@ -3757,6 +3762,7 @@ void qemu_init(int argc, char **argv, char **envp) qemu_create_late_backends(); + qemu_log("machine init start\n"); /* parse features once if machine provides default cpu_type */ current_machine->cpu_type = machine_class->default_cpu_type; if (cpu_option) { -- Gitee From 98cbb6d13484e79b6f9da064a40a281f2983be1d Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Wed, 9 Feb 2022 19:58:21 +0800 Subject: [PATCH 161/486] virtio-scsi: bugfix: fix qemu crash for hotplug scsi disk with dataplane The vm will trigger a disk sweep operation after plugging a controller who's io type is iothread. If attach a scsi disk immediately, the sg_inqury request in vm will trigger the assert in virtio_scsi_ctx_check(), which is called by virtio_scsi_handle_cmd_req_prepare(). Add judgment in virtio_scsi_handle_cmd_req_prepare() and return IO Error directly if the device has not been initialized. Signed-off-by: Jinhua Cao --- hw/scsi/virtio-scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 51fd09522a..781a37fe89 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -638,7 +638,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) req->req.cmd.tag, req->req.cmd.cdb[0]); d = virtio_scsi_device_get(s, req->req.cmd.lun); - if (!d) { + if (!d || !d->qdev.realized) { req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; virtio_scsi_complete_cmd_req(req); return -ENOENT; -- Gitee From cee545754b44b6283408ec6a43eb0e317c98ebb1 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Wed, 9 Feb 2022 20:27:41 +0800 Subject: [PATCH 162/486] virtio: net-tap: bugfix: del net client if net_init_tap_one failed In net_init_tap_one(), if the net-tap initializes successful but other actions failed during vhost-net hot-plugging, the net-tap will remain in the net clients.causing next hot-plug fails again. Signed-off-by: Jinhua Cao --- net/tap.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/tap.c b/net/tap.c index c5cbeaa7a2..3f79cd06c2 100644 --- a/net/tap.c +++ b/net/tap.c @@ -684,7 +684,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, tap_set_sndbuf(s->fd, tap, &err); if (err) { error_propagate(errp, err); - return; + goto fail; } if (tap->has_fd || tap->has_fds) { @@ -726,13 +726,13 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } else { warn_report_err(err); } - return; + goto fail; } ret = qemu_try_set_nonblock(vhostfd); if (ret < 0) { error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", name, fd); - return; + goto fail; } } else { vhostfd = open("/dev/vhost-net", O_RDWR); @@ -744,7 +744,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, warn_report("tap: open vhost char device failed: %s", strerror(errno)); } - return; + goto fail; } qemu_set_nonblock(vhostfd); } @@ -758,11 +758,17 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } else { warn_report(VHOST_NET_INIT_FAILED); } - return; + goto fail; } } else if (vhostfdname) { error_setg(errp, "vhostfd(s)= is not valid without vhost"); + goto fail; } + + return; + +fail: + qemu_del_net_client(&s->nc); } static int get_fds(char *str, char *fds[], int max) -- Gitee From 95d334a905e8ddaac4a8cec908dcdb03b2e5993f Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 10:17:20 +0800 Subject: [PATCH 163/486] virtio: bugfix: clean up callback when del virtqueue We will access NULL pointer as follow: 1. Start a vm with multiqueue vhost-net 2. then we write VIRTIO_PCI_GUEST_FEATURES in PCI configuration to trigger multiqueue disable in this vm which will delete the virtqueue. In this step, the tx_bh is deleted but the callback virtio_net_handle_tx_bh still exist. 3. Finally, we write VIRTIO_PCI_QUEUE_NOTIFY in PCI configuration to notify the deleted virtqueue. In this way, virtio_net_handle_tx_bh will be called and qemu will be crashed. Signed-off-by: Jinhua Cao --- hw/net/virtio-net.c | 5 ++++- hw/virtio/virtio.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f2014d5ea0..b3a5d0b19e 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2644,7 +2644,10 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) return; } virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(q->tx_bh); + + if (q->tx_bh) { + qemu_bh_schedule(q->tx_bh); + } } static void virtio_net_tx_timer(void *opaque) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9b4ac58a16..ec3e96af3b 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2417,6 +2417,7 @@ void virtio_delete_queue(VirtQueue *vq) { vq->vring.num = 0; vq->vring.num_default = 0; + vq->vring.align = 0; vq->handle_output = NULL; vq->handle_aio_output = NULL; g_free(vq->used_elems); -- Gitee From 532566ba64b60f2dd2f8ff41d670712ccafe1e98 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 10:31:38 +0800 Subject: [PATCH 164/486] virtio-net: bugfix: do not delete netdev before virtio net For the vhost-user net-card, it is allow to delete its network backend while the virtio-net device still exists. However, when the status of the device changes in guest, QEMU will check whether the network backend exists, otherwise it will crash. So do not allowed to delete the network backend directly without delete virtio-net device. Signed-off-by: Jinhua Cao --- net/net.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/net.c b/net/net.c index f0d14dbfc1..ed4b1c1740 100644 --- a/net/net.c +++ b/net/net.c @@ -1202,6 +1202,12 @@ void qmp_netdev_del(const char *id, Error **errp) return; } + if (nc->info->type == NET_CLIENT_DRIVER_VHOST_USER && nc->peer) { + error_setg(errp, "Device '%s' is a netdev for vhostuser," + "please delete the peer front-end device (virtio-net) first.", id); + return; + } + qemu_del_net_client(nc); /* -- Gitee From 318f0eda68554af0c779e5374f16bf8cdb895fe7 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 10:48:27 +0800 Subject: [PATCH 165/486] virtio-net: fix max vring buf size when set ring num Signed-off-by: Jinhua Cao --- hw/virtio/virtio.c | 9 +++++++-- include/hw/virtio/virtio.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ec3e96af3b..03afa36e99 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2241,12 +2241,17 @@ void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) { + int vq_max_size = VIRTQUEUE_MAX_SIZE; + + if (!strcmp(vdev->name, "virtio-net")) { + vq_max_size = VIRTIO_NET_VQ_MAX_SIZE; + } + /* Don't allow guest to flip queue between existent and * nonexistent states, or to set it to an invalid size. */ if (!!num != !!vdev->vq[n].vring.num || - num > VIRTQUEUE_MAX_SIZE || - num < 0) { + num > vq_max_size || num < 0) { return; } vdev->vq[n].vring.num = num; diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 8bab9cfb75..b3749ce34b 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -49,6 +49,7 @@ size_t virtio_feature_get_config_size(const VirtIOFeature *features, typedef struct VirtQueue VirtQueue; #define VIRTQUEUE_MAX_SIZE 1024 +#define VIRTIO_NET_VQ_MAX_SIZE (4096) typedef struct VirtQueueElement { -- Gitee From 9e04e1c6a7a12e3e1d0a8a7cf07f441597a1dbb7 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 11:09:36 +0800 Subject: [PATCH 166/486] virtio: check descriptor numbers Check if the vring num is normal in virtio_save(), and add LOG the vm push the wrong viring num down through writing IO Port. Signed-off-by: Jinhua Cao --- hw/virtio/virtio.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 03afa36e99..007f4c9e26 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2860,6 +2860,22 @@ static const VMStateDescription vmstate_virtio = { } }; +static void check_vring_avail_num(VirtIODevice *vdev, int index) +{ + uint16_t nheads; + + /* Check it isn't doing strange things with descriptor numbers. */ + nheads = vring_avail_idx(&vdev->vq[index]) - vdev->vq[index].last_avail_idx; + if (nheads > vdev->vq[index].vring.num) { + qemu_log("VQ %d size 0x%x Guest index 0x%x " + "inconsistent with Host index 0x%x: " + "delta 0x%x\n", + index, vdev->vq[index].vring.num, + vring_avail_idx(&vdev->vq[index]), + vdev->vq[index].last_avail_idx, nheads); + } +} + int virtio_save(VirtIODevice *vdev, QEMUFile *f) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -2890,6 +2906,8 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f) if (vdev->vq[i].vring.num == 0) break; + check_vring_avail_num(vdev, i); + qemu_put_be32(f, vdev->vq[i].vring.num); if (k->has_variable_vring_alignment) { qemu_put_be32(f, vdev->vq[i].vring.align); -- Gitee From 41aa66e37d04246d48b5417c57967425ecc466a0 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 11:16:26 +0800 Subject: [PATCH 167/486] virtio: bugfix: add rcu_read_lock when vring_avail_idx is called viring_avail_idx should be called within rcu_read_lock(), or may get NULL caches in vring_get_region_caches() and trigger assert(). Signed-off-by: Jinhua Cao --- hw/virtio/virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 007f4c9e26..0af9684881 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2864,6 +2864,7 @@ static void check_vring_avail_num(VirtIODevice *vdev, int index) { uint16_t nheads; + rcu_read_lock(); /* Check it isn't doing strange things with descriptor numbers. */ nheads = vring_avail_idx(&vdev->vq[index]) - vdev->vq[index].last_avail_idx; if (nheads > vdev->vq[index].vring.num) { @@ -2874,6 +2875,7 @@ static void check_vring_avail_num(VirtIODevice *vdev, int index) vring_avail_idx(&vdev->vq[index]), vdev->vq[index].last_avail_idx, nheads); } + rcu_read_unlock(); } int virtio_save(VirtIODevice *vdev, QEMUFile *f) -- Gitee From 8a08b3b41400e152cc1786ae5a8a53507f8e925c Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 14:16:17 +0800 Subject: [PATCH 168/486] virtio: print the guest virtio_net features that host does not support Signed-off-by: Jinhua Cao --- hw/net/virtio-net.c | 41 ++++++++++++++++++++++++++++++++++++++ hw/virtio/virtio.c | 7 +++++++ include/hw/virtio/virtio.h | 1 + 3 files changed, 49 insertions(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index b3a5d0b19e..6874c88bc0 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3692,6 +3692,46 @@ static Property virtio_net_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void virtio_net_print_features(uint64_t features) +{ + Property *props = virtio_net_properties; + int feature_cnt = 0; + + if (!features) { + return; + } + printf("virtio_net_feature: "); + + for (; features && props->name; props++) { + /* The bitnr of property may be default(0) besides 'csum' property. */ + if (props->bitnr == 0 && strcmp(props->name, "csum")) { + continue; + } + + /* Features only support 64bit. */ + if (props->bitnr > 63) { + continue; + } + + if (virtio_has_feature(features, props->bitnr)) { + virtio_clear_feature(&features, props->bitnr); + if (feature_cnt != 0) { + printf(", "); + } + printf("%s", props->name); + feature_cnt++; + } + } + + if (features) { + if (feature_cnt != 0) { + printf(", "); + } + printf("unkown bits 0x%." PRIx64, features); + } + printf("\n"); +} + static void virtio_net_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -3706,6 +3746,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->set_config = virtio_net_set_config; vdc->get_features = virtio_net_get_features; vdc->set_features = virtio_net_set_features; + vdc->print_features = virtio_net_print_features; vdc->bad_features = virtio_net_bad_features; vdc->reset = virtio_net_reset; vdc->set_status = virtio_net_set_status; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 0af9684881..9a2a83d507 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2967,6 +2967,13 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); bool bad = (val & ~(vdev->host_features)) != 0; + uint64_t feat = val & ~(vdev->host_features); + + if (bad && k->print_features) { + qemu_log("error: Please check host config, "\ + "because host does not support required feature bits 0x%" PRIx64 "\n", feat); + k->print_features(feat); + } val &= vdev->host_features; if (k->set_features) { diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index b3749ce34b..7472145821 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -127,6 +127,7 @@ struct VirtioDeviceClass { int (*validate_features)(VirtIODevice *vdev); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); + void (*print_features)(uint64_t features); void (*reset)(VirtIODevice *vdev); void (*set_status)(VirtIODevice *vdev, uint8_t val); /* For transitional devices, this is a bitmap of features -- Gitee From 74ab61b4317f12b231fb2cbcd54a333a07efd678 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 14:37:52 +0800 Subject: [PATCH 169/486] virtio: bugfix: check the value of caches before accessing it Vring caches may be NULL in check_vring_avail_num() if virtio_reset() is called at the same time, such as when the virtual machine starts. So check it before accessing it in vring_avail_idx(). Signed-off-by: Jinhua Cao --- hw/virtio/virtio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9a2a83d507..b08fff9419 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2863,8 +2863,19 @@ static const VMStateDescription vmstate_virtio = { static void check_vring_avail_num(VirtIODevice *vdev, int index) { uint16_t nheads; + VRingMemoryRegionCaches *caches; rcu_read_lock(); + caches = qatomic_rcu_read(&vdev->vq[index].vring.caches); + if (caches == NULL) { + /* + * caches may be NULL if virtio_reset is called at the same time, + * such as when the virtual machine starts. + */ + rcu_read_unlock(); + return; + } + /* Check it isn't doing strange things with descriptor numbers. */ nheads = vring_avail_idx(&vdev->vq[index]) - vdev->vq[index].last_avail_idx; if (nheads > vdev->vq[index].vring.num) { -- Gitee From 7beaecc21a8a573d43c7ad7604ac77cdf5bbf405 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Sat, 12 Feb 2022 17:22:38 +0800 Subject: [PATCH 170/486] virtio-net: set the max of queue size to 4096 Signed-off-by: Jinhua Cao --- hw/net/virtio-net.c | 10 +++++----- hw/virtio/virtio.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 6874c88bc0..009dc9f3d1 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -637,7 +637,7 @@ static int virtio_net_max_tx_queue_size(VirtIONet *n) return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; } - return VIRTQUEUE_MAX_SIZE; + return VIRTIO_NET_VQ_MAX_SIZE; } static int peer_attach(VirtIONet *n, int index) @@ -3394,23 +3394,23 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) * help from us (using virtio 1 and up). */ if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || - n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || + n->net_conf.rx_queue_size > VIRTIO_NET_VQ_MAX_SIZE || !is_power_of_2(n->net_conf.rx_queue_size)) { error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " "must be a power of 2 between %d and %d.", n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, - VIRTQUEUE_MAX_SIZE); + VIRTIO_NET_VQ_MAX_SIZE); virtio_cleanup(vdev); return; } if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE || - n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE || + n->net_conf.tx_queue_size > VIRTIO_NET_VQ_MAX_SIZE || !is_power_of_2(n->net_conf.tx_queue_size)) { error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), " "must be a power of 2 between %d and %d", n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE, - VIRTQUEUE_MAX_SIZE); + VIRTIO_NET_VQ_MAX_SIZE); virtio_cleanup(vdev); return; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index b08fff9419..120672672e 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2401,7 +2401,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, break; } - if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) { + if (i == VIRTIO_QUEUE_MAX) { qemu_log("unacceptable queue_size (%d) or num (%d)\n", queue_size, i); abort(); -- Gitee From 88dfb4236c735c608f8ca91cfbfb5ac424d654aa Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 17:28:49 +0800 Subject: [PATCH 171/486] virtio-net: update the default and max of rx/tx_queue_size Set the max of tx_queue_size to 4096 even if the backends are not vhost-user. Set the default of rx/tx_queue_size to 2048 if the backends are vhost-user, otherwise to 4096. Signed-off-by: Jinhua Cao --- hw/net/virtio-net.c | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 009dc9f3d1..e887589a30 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -51,12 +51,11 @@ #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ /* previously fixed value */ -#define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 -#define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 +#define VIRTIO_NET_VHOST_USER_DEFAULT_SIZE 2048 /* for now, only allow larger queue_pairs; with virtio-1, guest can downsize */ -#define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE -#define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE +#define VIRTIO_NET_RX_QUEUE_MIN_SIZE 256 +#define VIRTIO_NET_TX_QUEUE_MIN_SIZE 256 #define VIRTIO_NET_IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */ @@ -622,6 +621,28 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, } } +static void virtio_net_set_default_queue_size(VirtIONet *n) +{ + NetClientState *peer = n->nic_conf.peers.ncs[0]; + + /* Default value is 0 if not set */ + if (n->net_conf.rx_queue_size == 0) { + if (peer && peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + n->net_conf.rx_queue_size = VIRTIO_NET_VHOST_USER_DEFAULT_SIZE; + } else { + n->net_conf.rx_queue_size = VIRTIO_NET_VQ_MAX_SIZE; + } + } + + if (n->net_conf.tx_queue_size == 0) { + if (peer && peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + n->net_conf.tx_queue_size = VIRTIO_NET_VHOST_USER_DEFAULT_SIZE; + } else { + n->net_conf.tx_queue_size = VIRTIO_NET_VQ_MAX_SIZE; + } + } +} + static int virtio_net_max_tx_queue_size(VirtIONet *n) { NetClientState *peer = n->nic_conf.peers.ncs[0]; @@ -630,11 +651,11 @@ static int virtio_net_max_tx_queue_size(VirtIONet *n) * Backends other than vhost-user don't support max queue size. */ if (!peer) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + return VIRTIO_NET_VQ_MAX_SIZE; } if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + return VIRTIO_NET_VQ_MAX_SIZE; } return VIRTIO_NET_VQ_MAX_SIZE; @@ -3388,6 +3409,8 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) virtio_net_set_config_size(n, n->host_features); virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); + virtio_net_set_default_queue_size(n); + /* * We set a lower limit on RX queue size to what it always was. * Guests that want a smaller ring can always resize it without @@ -3679,10 +3702,8 @@ static Property virtio_net_properties[] = { TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), - DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, - VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), - DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, - VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE), + DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, 0), + DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, 0), DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0), DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend, true), -- Gitee From a9459c849c5484a022f67a317b72de764c84c845 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 20:21:33 +0800 Subject: [PATCH 172/486] vhost-user: add unregister_savevm when vhost-user cleanup Signed-off-by: Jinhua Cao --- hw/virtio/vhost-user.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index bf6e50223c..c265e9e92c 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -24,6 +24,7 @@ #include "sysemu/cryptodev.h" #include "migration/migration.h" #include "migration/postcopy-ram.h" +#include "migration/register.h" #include "trace.h" #include @@ -2068,6 +2069,7 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev) u->region_rb_len = 0; g_free(u); dev->opaque = 0; + unregister_savevm(NULL, "vhost-user", dev); return 0; } -- Gitee From 724134432ef21f1fb2b18bbe55b891d31181ccca Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 14:25:39 +0800 Subject: [PATCH 173/486] qemu-img: block: dont blk_make_zero if discard_zeroes false Signed-off-by: Jinhua Cao --- block/file-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/file-posix.c b/block/file-posix.c index b283093e5b..aed7529f44 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -804,7 +804,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; + bs->supported_zero_flags = s->discard_zeroes ? (BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) : 0; if (S_ISREG(st.st_mode)) { /* When extending regular files, we get zeros from the OS */ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; -- Gitee From 3a223111d71307eb4fdc18f5ee46ce3d6cb57660 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 18:05:47 +0800 Subject: [PATCH 174/486] vhost-user: Add support reconnect vhost-user socket Add support reconnect vhost-user socket, the reconnect time is set to be 3 seconds. Signed-off-by: Jinhua Cao --- chardev/char-socket.c | 19 ++++++++++++++++++- hw/net/vhost_net.c | 4 +++- hw/virtio/vhost-user.c | 6 ++++++ include/chardev/char.h | 16 ++++++++++++++++ net/vhost-user.c | 3 +++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 836cfa0bc2..b1e9f43ec6 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -393,6 +393,22 @@ static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond) return qio_channel_create_watch(s->ioc, cond); } +static void tcp_chr_set_reconnect_time(Chardev *chr, + int64_t reconnect_time) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + s->reconnect_time = reconnect_time; +} + +void qemu_chr_set_reconnect_time(Chardev *chr, int64_t reconnect_time) +{ + ChardevClass *cc = CHARDEV_GET_CLASS(chr); + + if (cc->chr_set_reconnect_time) { + cc->chr_set_reconnect_time(chr, reconnect_time); + } +} + static void remove_hup_source(SocketChardev *s) { if (s->hup_source != NULL) { @@ -591,7 +607,7 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) { qio_channel_set_blocking(s->ioc, false, NULL); } - if (size == 0) { + if (size == 0 && chr->chr_for_flag != CHR_FOR_VHOST_USER) { /* connection closed */ tcp_chr_disconnect(chr); } @@ -1585,6 +1601,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data) cc->set_msgfds = tcp_set_msgfds; cc->chr_add_client = tcp_chr_add_client; cc->chr_add_watch = tcp_chr_add_watch; + cc->chr_set_reconnect_time = tcp_chr_set_reconnect_time; cc->chr_update_read_handler = tcp_chr_update_read_handler; object_class_property_add(oc, "addr", "SocketAddress", diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 30379d2ca4..a60f7cef9a 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -376,7 +376,9 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, goto err_start; } - if (peer->vring_enable) { + /* ovs needs to restore all states of vring */ + if (peer->vring_enable || + ncs[i].peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { /* restore vring enable state */ r = vhost_set_vring_enable(peer, peer->vring_enable); diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index c265e9e92c..fc2b1b81c9 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1926,9 +1926,15 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, uint64_t features, protocol_features, ram_slots; struct vhost_user *u; int err; + Chardev *chr; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + chr = qemu_chr_fe_get_driver(((VhostUserState *)opaque)->chr); + if (chr) { + chr->chr_for_flag = CHR_FOR_VHOST_USER; + } + u = g_new0(struct vhost_user, 1); u->user = opaque; u->dev = dev; diff --git a/include/chardev/char.h b/include/chardev/char.h index a319b5fdff..f388d4b109 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -14,6 +14,8 @@ #define IAC_SB 250 #define IAC 255 +#define CHR_FOR_VHOST_USER 0x32a1 + /* character device */ typedef struct CharBackend CharBackend; @@ -70,6 +72,7 @@ struct Chardev { GSource *gsource; GMainContext *gcontext; DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST); + int chr_for_flag; }; /** @@ -227,6 +230,16 @@ int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all); #define qemu_chr_write_all(s, buf, len) qemu_chr_write(s, buf, len, true) int qemu_chr_wait_connected(Chardev *chr, Error **errp); +/** + * @qemu_chr_set_reconnect_time: + * + * Set reconnect time for char disconnect. + * Currently, only vhost user will call it. + * + * @reconnect_time the reconnect_time to be set + */ +void qemu_chr_set_reconnect_time(Chardev *chr, int64_t reconnect_time); + #define TYPE_CHARDEV "chardev" OBJECT_DECLARE_TYPE(Chardev, ChardevClass, CHARDEV) @@ -306,6 +319,9 @@ struct ChardevClass { /* handle various events */ void (*chr_be_event)(Chardev *s, QEMUChrEvent event); + + /* set reconnect time */ + void (*chr_set_reconnect_time)(Chardev *chr, int64_t reconnect_time); }; Chardev *qemu_chardev_new(const char *id, const char *typename, diff --git a/net/vhost-user.c b/net/vhost-user.c index b1a0247b59..d1aefcb9aa 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -21,6 +21,8 @@ #include "qemu/option.h" #include "trace.h" +#define VHOST_USER_RECONNECT_TIME (3) + typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ @@ -287,6 +289,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) trace_vhost_user_event(chr->label, event); switch (event) { case CHR_EVENT_OPENED: + qemu_chr_set_reconnect_time(chr, VHOST_USER_RECONNECT_TIME); if (vhost_user_start(queues, ncs, s->vhost_user) < 0) { qemu_chr_fe_disconnect(&s->chr); return; -- Gitee From 12af29806ba8ede96567e4df9223f0c02669727c Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 18:49:21 +0800 Subject: [PATCH 175/486] vhost-user: Set the acked_features to vm's featrue Fix the problem when vm restart, the ovs restart and lead to the net unreachable. The soluation is set the acked_features to vm's featrue just the same as guest virtio-net mod load. Signed-off-by: Jinhua Cao --- hw/net/vhost_net.c | 58 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index a60f7cef9a..e8a79db94d 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -152,9 +152,26 @@ static int vhost_net_get_fd(NetClientState *backend) } } +static uint64_t vhost_get_mask_features(const int *feature_bits, uint64_t features) +{ + const int *bit = feature_bits; + uint64_t out_features = 0; + + while (*bit != VHOST_INVALID_FEATURE_BIT) { + uint64_t bit_mask = (1ULL << *bit); + if (features & bit_mask) { + out_features |= bit_mask; + } + bit++; + } + return out_features; +} + struct vhost_net *vhost_net_init(VhostNetOptions *options) { int r; + VirtIONet *n; + VirtIODevice *vdev; bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL; struct vhost_net *net = g_new0(struct vhost_net, 1); uint64_t features = 0; @@ -180,7 +197,46 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->backend = r; net->dev.protocol_features = 0; } else { - net->dev.backend_features = 0; + /* for ovs restart when vm start. + * Normal situation: + * 1.vm start. + * 2.vhost_net_init init ok, then dev.acked_features is 0x40000000. + * 3.guest virtio-net mod load. qemu will call virtio_net_set_features set + * dev.acked_features to 0x40408000. + * 4.feature set to ovs's vhostuser(0x40408000). + * 5.ovs restart. + * 6.vhost_user_stop will save net->dev.acked_features(0x40408000) to + * VhostUserState's acked_features(0x40408000). + * 7.restart ok. + * 8.vhost_net_init fun call vhost_user_get_acked_features get the save + * features, and set to net->dev.acked_features. + * Abnormal situation: + * 1.vm start. + * 2.vhost_net_init init ok, then dev.acked_features is 0x40000000. + * 3.ovs restart. + * 4.vhost_user_stop will save net->dev.acked_features(0x40000000) to + * VhostUserState's acked_features(0x40000000). + * 5.guest virtio-net mod load. qemu will call virtio_net_set_features set + * dev.acked_features to 0x40408000. + * 6.restart ok. + * 7.vhost_net_init fun call vhost_user_get_acked_features get the save + * features(0x40000000), and set to net->dev.acked_features(0x40000000). + * 8.feature set to ovs's vhostuser(0x40000000). + * + * in abnormal situation, qemu set the wrong features to ovs's vhostuser, + * then the vm's network will be down. + * in abnormal situation, we found it just lost the guest feartures in + * acked_features, so hear we set the acked_features to vm's featrue + * just the same as guest virtio-net mod load. + */ + if (options->net_backend->peer) { + n = qemu_get_nic_opaque(options->net_backend->peer); + vdev = VIRTIO_DEVICE(n); + net->dev.backend_features = vhost_get_mask_features(vhost_net_get_feature_bits(net), + vdev->guest_features); + } else { + net->dev.backend_features = 0; + } net->dev.protocol_features = 0; net->backend = -1; -- Gitee From 5c753d539a968f2127ff6e5b916cd4b38a08b40c Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 18:59:34 +0800 Subject: [PATCH 176/486] vhost-user: add vhost_set_mem_table when vm load_setup at destination When migrate huge vm, packages lost are 90+. During the load_setup of the destination vm, pass the vm mem structure to ovs, the netcard could be enabled when the migration finish state shifting. Signed-off-by: Jinhua Cao --- hw/virtio/vhost-user.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index fc2b1b81c9..a8feea489b 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1920,6 +1920,28 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, return 0; } +static int vhost_user_load_setup(QEMUFile *f, void *opaque) +{ + struct vhost_dev *hdev = opaque; + int r; + + if (hdev->vhost_ops && hdev->vhost_ops->vhost_set_mem_table) { + r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); + if (r < 0) { + qemu_log("error: vhost_set_mem_table failed: %s(%d)\n", + strerror(errno), errno); + return r; + } else { + qemu_log("info: vhost_set_mem_table OK\n"); + } + } + return 0; +} + +SaveVMHandlers savevm_vhost_user_handlers = { + .load_setup = vhost_user_load_setup, +}; + static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, Error **errp) { @@ -2044,6 +2066,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, u->postcopy_notifier.notify = vhost_user_postcopy_notifier; postcopy_add_notifier(&u->postcopy_notifier); + register_savevm_live("vhost-user", -1, 1, &savevm_vhost_user_handlers, dev); return 0; } -- Gitee From 185d7efe768229b43911504f64fccd33ad3650ef Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 19:17:59 +0800 Subject: [PATCH 177/486] vhost-user: add separate memslot counter for vhost-user Used_memslots is equal to dev->mem->nregions now, it is true for vhost kernel, but not for vhost user, which uses the memory regions that have file descriptor. In fact, not all of the memory regions have file descriptor. It is usefully in some scenarios, e.g. used_memslots is 8, and only 5 memory slots can be used by vhost user, it is failed to hot plug a new memory RAM because vhost_has_free_slot just returned false, but we can hot plug it safely in fact. Signed-off-by: Jinhua Cao --- hw/virtio/vhost-backend.c | 14 ++++++++++ hw/virtio/vhost-user.c | 27 ++++++++++++++++++ hw/virtio/vhost.c | 46 +++++++++++++++++++++++++------ include/hw/virtio/vhost-backend.h | 4 +++ 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index b65f8f7e97..2acfb750fd 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -20,6 +20,8 @@ #include #include +static unsigned int vhost_kernel_used_memslots; + static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, void *arg) { @@ -293,6 +295,16 @@ static void vhost_kernel_set_iotlb_callback(struct vhost_dev *dev, qemu_set_fd_handler((uintptr_t)dev->opaque, NULL, NULL, NULL); } +static void vhost_kernel_set_used_memslots(struct vhost_dev *dev) +{ + vhost_kernel_used_memslots = dev->mem->nregions; +} + +static unsigned int vhost_kernel_get_used_memslots(void) +{ + return vhost_kernel_used_memslots; +} + const VhostOps kernel_ops = { .backend_type = VHOST_BACKEND_TYPE_KERNEL, .vhost_backend_init = vhost_kernel_init, @@ -325,6 +337,8 @@ const VhostOps kernel_ops = { #endif /* CONFIG_VHOST_VSOCK */ .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, + .vhost_set_used_memslots = vhost_kernel_set_used_memslots, + .vhost_get_used_memslots = vhost_kernel_get_used_memslots, }; #endif diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index a8feea489b..176cae9244 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -234,6 +234,7 @@ static VhostUserMsg m __attribute__ ((unused)); /* The version of the protocol we support */ #define VHOST_USER_VERSION (0x1) +static unsigned int vhost_user_used_memslots; struct vhost_user { struct vhost_dev *dev; @@ -2524,6 +2525,30 @@ void vhost_user_cleanup(VhostUserState *user) user->chr = NULL; } +static void vhost_user_set_used_memslots(struct vhost_dev *dev) +{ + unsigned int counter = 0; + int i; + + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + ram_addr_t offset; + MemoryRegion *mr; + + mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr, + &offset); + if (mr && memory_region_get_fd(mr) > 0) { + counter++; + } + } + vhost_user_used_memslots = counter; +} + +static unsigned int vhost_user_get_used_memslots(void) +{ + return vhost_user_used_memslots; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_backend_init, @@ -2557,4 +2582,6 @@ const VhostOps user_ops = { .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, .vhost_get_inflight_fd = vhost_user_get_inflight_fd, .vhost_set_inflight_fd = vhost_user_set_inflight_fd, + .vhost_set_used_memslots = vhost_user_set_used_memslots, + .vhost_get_used_memslots = vhost_user_get_used_memslots, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index dafb23c481..e4809777bc 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -45,20 +45,20 @@ static struct vhost_log *vhost_log; static struct vhost_log *vhost_log_shm; -static unsigned int used_memslots; static QLIST_HEAD(, vhost_dev) vhost_devices = QLIST_HEAD_INITIALIZER(vhost_devices); bool vhost_has_free_slot(void) { - unsigned int slots_limit = ~0U; struct vhost_dev *hdev; QLIST_FOREACH(hdev, &vhost_devices, entry) { - unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); - slots_limit = MIN(slots_limit, r); + if (hdev->vhost_ops->vhost_get_used_memslots() >= + hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { + return false; + } } - return slots_limit > used_memslots; + return true; } static void vhost_dev_sync_region(struct vhost_dev *dev, @@ -521,7 +521,6 @@ static void vhost_commit(MemoryListener *listener) dev->n_mem_sections * sizeof dev->mem->regions[0]; dev->mem = g_realloc(dev->mem, regions_size); dev->mem->nregions = dev->n_mem_sections; - used_memslots = dev->mem->nregions; for (i = 0; i < dev->n_mem_sections; i++) { struct vhost_memory_region *cur_vmr = dev->mem->regions + i; struct MemoryRegionSection *mrs = dev->mem_sections + i; @@ -697,6 +696,7 @@ static void vhost_region_add_section(struct vhost_dev *dev, dev->tmp_sections[dev->n_tmp_sections - 1].fv = NULL; memory_region_ref(section->mr); } + dev->vhost_ops->vhost_set_used_memslots(dev); } /* Used for both add and nop callbacks */ @@ -712,6 +712,17 @@ static void vhost_region_addnop(MemoryListener *listener, vhost_region_add_section(dev, section); } +static void vhost_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + struct vhost_dev *dev = container_of(listener, struct vhost_dev, + memory_listener); + if (!vhost_section(dev, section)) { + return; + } + dev->vhost_ops->vhost_set_used_memslots(dev); +} + static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) { struct vhost_iommu *iommu = container_of(n, struct vhost_iommu, n); @@ -1319,6 +1330,18 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) event_notifier_cleanup(&vq->masked_notifier); } +static bool vhost_dev_used_memslots_is_exceeded(struct vhost_dev *hdev) +{ + if (hdev->vhost_ops->vhost_get_used_memslots() > + hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { + error_report("vhost backend memory slots limit is less" + " than current number of present memory slots"); + return true; + } + + return false; +} + int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout, Error **errp) @@ -1374,6 +1397,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .name = "vhost", .begin = vhost_begin, .commit = vhost_commit, + .region_del = vhost_region_del, .region_add = vhost_region_addnop, .region_nop = vhost_region_addnop, .log_start = vhost_log_start, @@ -1420,9 +1444,13 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, memory_listener_register(&hdev->memory_listener, &address_space_memory); QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); - if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { - error_setg(errp, "vhost backend memory slots limit is less" - " than current number of present memory slots"); + /* + * If we started a VM without any vhost device, + * vhost_dev_used_memslots_is_exceeded will always return false for the + * first time vhost device hot-plug(vhost_get_used_memslots is always 0), + * so it needs to double check here + */ + if (vhost_dev_used_memslots_is_exceeded(hdev)) { r = -EINVAL; goto fail_busyloop; } diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 81bf3109f8..a64708f456 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -125,6 +125,8 @@ typedef int (*vhost_vq_get_addr_op)(struct vhost_dev *dev, typedef int (*vhost_get_device_id_op)(struct vhost_dev *dev, uint32_t *dev_id); typedef bool (*vhost_force_iommu_op)(struct vhost_dev *dev); +typedef void (*vhost_set_used_memslots_op)(struct vhost_dev *dev); +typedef unsigned int (*vhost_get_used_memslots_op)(void); typedef struct VhostOps { VhostBackendType backend_type; @@ -171,6 +173,8 @@ typedef struct VhostOps { vhost_vq_get_addr_op vhost_vq_get_addr; vhost_get_device_id_op vhost_get_device_id; vhost_force_iommu_op vhost_force_iommu; + vhost_set_used_memslots_op vhost_set_used_memslots; + vhost_get_used_memslots_op vhost_get_used_memslots; } VhostOps; int vhost_backend_update_device_iotlb(struct vhost_dev *dev, -- Gitee From f46191f24706a6200cfe607a902b3da45f57c9ad Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 19:24:30 +0800 Subject: [PATCH 178/486] vhost-user: quit infinite loop while used memslots is more than the backend limit When used memslots is more than the backend limit, the vhost-user netcard would attach fail and quit infinite loop. Signed-off-by: Jinhua Cao --- hw/virtio/vhost.c | 9 +++++++++ include/hw/virtio/vhost.h | 1 + net/vhost-user.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index e4809777bc..4c4072951c 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -48,6 +48,8 @@ static struct vhost_log *vhost_log_shm; static QLIST_HEAD(, vhost_dev) vhost_devices = QLIST_HEAD_INITIALIZER(vhost_devices); +bool used_memslots_exceeded; + bool vhost_has_free_slot(void) { struct vhost_dev *hdev; @@ -1336,9 +1338,11 @@ static bool vhost_dev_used_memslots_is_exceeded(struct vhost_dev *hdev) hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { error_report("vhost backend memory slots limit is less" " than current number of present memory slots"); + used_memslots_exceeded = true; return true; } + used_memslots_exceeded = false; return false; } @@ -1895,3 +1899,8 @@ int vhost_net_set_backend(struct vhost_dev *hdev, return -1; } + +bool used_memslots_is_exceeded(void) +{ + return used_memslots_exceeded; +} diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 58a73e7b7a..86f36f0106 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -154,4 +154,5 @@ int vhost_dev_set_inflight(struct vhost_dev *dev, struct vhost_inflight *inflight); int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, struct vhost_inflight *inflight); +bool used_memslots_is_exceeded(void); #endif diff --git a/net/vhost-user.c b/net/vhost-user.c index d1aefcb9aa..f910a286e4 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -20,6 +20,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "trace.h" +#include "include/hw/virtio/vhost.h" #define VHOST_USER_RECONNECT_TIME (3) @@ -369,6 +370,11 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, nc0->name, NULL, true); + if (used_memslots_is_exceeded()) { + error_report("used memslots exceeded the backend limit, quit " + "loop"); + goto err; + } } while (!s->started); assert(s->vhost_net); -- Gitee From 1545a60a8b78490c7dc8909b7012bca63dba63cd Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Sat, 12 Feb 2022 15:41:08 +0800 Subject: [PATCH 179/486] qmp: add command to query used memslots of vhost-net and vhost-user Signed-off-by: Jinhua Cao --- hw/virtio/vhost-backend.c | 2 +- hw/virtio/vhost-user.c | 2 +- include/hw/virtio/vhost-backend.h | 2 ++ monitor/qmp-cmds.c | 12 ++++++++++++ qapi/net.json | 18 ++++++++++++++++++ qapi/pragma.json | 4 +++- 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 2acfb750fd..d8e1710758 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -300,7 +300,7 @@ static void vhost_kernel_set_used_memslots(struct vhost_dev *dev) vhost_kernel_used_memslots = dev->mem->nregions; } -static unsigned int vhost_kernel_get_used_memslots(void) +unsigned int vhost_kernel_get_used_memslots(void) { return vhost_kernel_used_memslots; } diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 176cae9244..8f69a3b850 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2544,7 +2544,7 @@ static void vhost_user_set_used_memslots(struct vhost_dev *dev) vhost_user_used_memslots = counter; } -static unsigned int vhost_user_get_used_memslots(void) +unsigned int vhost_user_get_used_memslots(void) { return vhost_user_used_memslots; } diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index a64708f456..7bbc658161 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -190,4 +190,6 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); +unsigned int vhost_kernel_get_used_memslots(void); +unsigned int vhost_user_get_used_memslots(void); #endif /* VHOST_BACKEND_H */ diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 98868cee03..f3e80ec8a7 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -36,6 +36,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-ui.h" +#include "qapi/qapi-commands-net.h" #include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" #include "exec/ramlist.h" @@ -43,6 +44,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" +#include "hw/virtio/vhost-backend.h" NameInfo *qmp_query_name(Error **errp) { @@ -471,3 +473,13 @@ int64_t qmp_query_rtc_date_diff(Error **errp) { return get_rtc_date_diff(); } + +uint32_t qmp_query_vhost_kernel_used_memslots(Error **errp) +{ + return vhost_kernel_get_used_memslots(); +} + +uint32_t qmp_query_vhost_user_used_memslots(Error **errp) +{ + return vhost_user_get_used_memslots(); +} diff --git a/qapi/net.json b/qapi/net.json index 7fab2e7cd8..c9ff849eed 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -696,3 +696,21 @@ ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } + +## +# @query-vhost-kernel-used-memslots: +# +# Get vhost-kernel nic used memslots +# +# Since: 4.1 +## +{ 'command': 'query-vhost-kernel-used-memslots', 'returns': 'uint32' } + +## +# @query-vhost-user-used-memslots: +# +# Get vhost-user nic used memslots +# +# Since: 4.1 +## +{ 'command': 'query-vhost-user-used-memslots', 'returns': 'uint32' } diff --git a/qapi/pragma.json b/qapi/pragma.json index b37f6de445..d35c897acb 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -27,7 +27,9 @@ 'query-tpm-models', 'query-tpm-types', 'ringbuf-read', - 'query-rtc-date-diff' ], + 'query-rtc-date-diff', + 'query-vhost-user-used-memslots', + 'query-vhost-kernel-used-memslots' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status -- Gitee From 8c52233c08fe66b2e5c79fd514d4f804aa6fe427 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 20:13:50 +0800 Subject: [PATCH 180/486] vhost-user-scsi: add support for SPDK hot upgrade In the hot upgrade scenario, the reconnection mechanism of qemu and SPDK after upgrade Signed-off-by: Jinhua Cao --- hw/scsi/vhost-user-scsi.c | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 1b2f7eed98..052740a76e 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -29,6 +29,9 @@ #include "hw/virtio/virtio-access.h" #include "chardev/char-fe.h" #include "sysemu/sysemu.h" +#include "qemu/log.h" + +#define VHOST_USER_SCSI_RECONNECT_TIME 3 /* Features supported by the host application */ static const int user_feature_bits[] = { @@ -59,7 +62,7 @@ static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) ret = vhost_scsi_common_start(vsc); if (ret < 0) { error_report("unable to start vhost-user-scsi: %s", strerror(-ret)); - exit(1); + return; } } else { vhost_scsi_common_stop(vsc); @@ -89,11 +92,43 @@ static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) { } +static void vhost_user_scsi_event(void *opaque, QEMUChrEvent event) +{ + int ret; + VHostUserSCSI *s = (VHostUserSCSI *)opaque; + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + qemu_log("event:%d, vdev status:%d\n", event, vdev->status); + + /* if CHR_EVENT_CLOSED, do nothing */ + if (event != CHR_EVENT_OPENED) { + return; + }; + + /* if status of vdev is not DRIVER_OK, just waiting. + * vsc should start when status change to DRIVER_OK */ + if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return; + } + + /* vsc may not fully start because of vhost app stopping */ + if (vsc->dev.started) { + vhost_scsi_common_stop(vsc); + } + + ret = vhost_scsi_common_start(vsc); + if (ret < 0) { + qemu_log("unable to start vhost-user-scsi: %s\n", strerror(-ret)); + } +} + static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + Chardev *chr; struct vhost_virtqueue *vqs = NULL; Error *err = NULL; int ret; @@ -132,6 +167,11 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) vsc->lun = 0; vsc->target = vs->conf.boot_tpgt; + chr = qemu_chr_fe_get_driver(&vs->conf.chardev); + qemu_chr_set_reconnect_time(chr, VHOST_USER_SCSI_RECONNECT_TIME); + qemu_chr_fe_set_handlers(&vs->conf.chardev, NULL, NULL, + vhost_user_scsi_event, NULL, s, NULL, true); + return; free_vhost: -- Gitee From 3c283ea7ca1902b9d221897fd65c5edb1d16e004 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 11 Feb 2022 20:33:47 +0800 Subject: [PATCH 181/486] i6300esb watchdog: bugfix: Add a runstate transition QEMU will abort() for the reasons now: invalid runstate transition: 'prelaunch' -> 'postmigrate' Aborted This happens when: |<- watchdog timeout happened, then sets reset_requested to | SHUTDOWN_CAUSE_GUEST_RESET; |<- hot-migration thread sets vm state to RUN_STATE_FINISH_MIGRATE | before the last time of migration; |<- main thread gets the change of reset_requested and triggers | reset, then sets vm state to RUN_STATE_PRELAUNCH; |<- hot-migration thread sets vm state to RUN_STATE_POSTMIGRATE. Then 'prelaunch' -> 'postmigrate' runstate transition will happen. It is legal so add this transition to runstate_transitions_def. Signed-off-by: Jinhua Cao --- softmmu/runstate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/softmmu/runstate.c b/softmmu/runstate.c index 5736d908db..680994cdf8 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -115,6 +115,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING }, { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE }, + { RUN_STATE_PRELAUNCH, RUN_STATE_POSTMIGRATE }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PAUSED }, -- Gitee From 8832a7dcad4e09229537781ff8db98496aa6f533 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Thu, 10 Feb 2022 21:27:53 +0800 Subject: [PATCH 182/486] qemu-img: add qemu-img direct create Introdue buffer_size while creating raw file, then we can controll the speed of direct write by: qemu-img create -t 'cache' -o buffer_size='num' Signed-off-by: Jinhua Cao --- block/file-posix.c | 65 +++++++++++++++++++++-- include/block/block_int.h | 2 + qapi/block-core.json | 6 ++- qemu-img-cmds.hx | 4 +- qemu-img.c | 14 ++++- tests/qemu-iotests/049.out | 102 ++++++++++++++++++------------------- tests/qemu-iotests/099.out | 2 +- 7 files changed, 134 insertions(+), 61 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index aed7529f44..5180fd1d0b 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -128,6 +128,10 @@ #define FTYPE_CD 1 #define MAX_BLOCKSIZE 4096 +#define DEFAULT_BUFFER_SIZE 65536 +#define BUFFER_ALIGN_SIZE 65536 +#define MIN_BUFFER_SIZE 65536 +#define MAX_BUFFER_SIZE 16777216 /* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes, * leaving a few more bytes for its future use. */ @@ -206,6 +210,8 @@ typedef struct RawPosixAIOData { off_t aio_offset; uint64_t aio_nbytes; + size_t buffer_size; + union { struct { struct iovec *iov; @@ -2218,7 +2224,8 @@ static void raw_close(BlockDriverState *bs) */ static int coroutine_fn raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, - PreallocMode prealloc, Error **errp) + PreallocMode prealloc, size_t buffer_size, + Error **errp) { RawPosixAIOData acb; @@ -2227,6 +2234,7 @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, .aio_fildes = fd, .aio_type = QEMU_AIO_TRUNCATE, .aio_offset = offset, + .buffer_size = buffer_size, .truncate = { .prealloc = prealloc, .errp = errp, @@ -2252,7 +2260,8 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, if (S_ISREG(st.st_mode)) { /* Always resizes to the exact @offset */ - return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); + return raw_regular_truncate(bs, s->fd, offset, prealloc, + DEFAULT_BUFFER_SIZE, errp); } if (prealloc != PREALLOC_MODE_OFF) { @@ -2465,6 +2474,8 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) int fd; uint64_t perm, shared; int result = 0; + int flags = O_RDWR | O_BINARY; + size_t buffer_size = DEFAULT_BUFFER_SIZE; /* Validate options and set default values */ assert(options->driver == BLOCKDEV_DRIVER_FILE); @@ -2484,9 +2495,19 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) error_setg(errp, "Extent size hint is too large"); goto out; } + if (!file_opts->has_cache) { + file_opts->cache = g_strdup("writeback"); + } + if (file_opts->preallocation == PREALLOC_MODE_FULL && + !strcmp(file_opts->cache, "none")) { + flags |= O_DIRECT; + } + if (file_opts->has_buffersize) { + buffer_size = file_opts->buffersize; + } /* Create file */ - fd = qemu_create(file_opts->filename, O_RDWR | O_BINARY, 0644, errp); + fd = qemu_create(file_opts->filename, flags, 0644, errp); if (fd < 0) { result = -errno; goto out; @@ -2521,7 +2542,8 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) } /* Clear the file by truncating it to 0 */ - result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); + result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, + buffer_size, errp); if (result < 0) { goto out_unlock; } @@ -2565,7 +2587,8 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) /* Resize and potentially preallocate the file to the desired * final size */ result = raw_regular_truncate(NULL, fd, file_opts->size, - file_opts->preallocation, errp); + file_opts->preallocation, + buffer_size, errp); if (result < 0) { goto out_unlock; } @@ -2586,6 +2609,7 @@ out_close: error_setg_errno(errp, -result, "Could not close the new file"); } out: + g_free(file_opts->cache); return result; } @@ -2602,6 +2626,8 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv, PreallocMode prealloc; char *buf = NULL; Error *local_err = NULL; + size_t buffersize = DEFAULT_BUFFER_SIZE; + char *cache = NULL; /* Skip file: protocol prefix */ strstart(filename, "file:", &filename); @@ -2624,6 +2650,21 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv, return -EINVAL; } + buffersize = qemu_opt_get_size_del(opts, BLOCK_OPT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE); + if (buffersize < MIN_BUFFER_SIZE || buffersize > MAX_BUFFER_SIZE) { + error_setg_errno(errp, EINVAL, "Buffer size must be between %d " + "and %d", MIN_BUFFER_SIZE, MAX_BUFFER_SIZE); + return -EINVAL; + } + + cache = qemu_opt_get_del(opts, BLOCK_OPT_CACHE); + if (!cache) { + cache = g_strdup("writeback"); + } + + buffersize = ROUND_UP(buffersize, BUFFER_ALIGN_SIZE); + options = (BlockdevCreateOptions) { .driver = BLOCKDEV_DRIVER_FILE, .u.file = { @@ -2635,6 +2676,10 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv, .nocow = nocow, .has_extent_size_hint = has_extent_size_hint, .extent_size_hint = extent_size_hint, + .has_buffersize = true, + .buffersize = buffersize, + .has_cache = true, + .cache = cache, }, }; return raw_co_create(&options, errp); @@ -3133,6 +3178,16 @@ static QemuOptsList raw_create_opts = { .type = QEMU_OPT_SIZE, .help = "Extent size hint for the image file, 0 to disable" }, + { + .name = BLOCK_OPT_CACHE, + .type = QEMU_OPT_STRING, + .help = "Cache mode (allowed values: writeback, none)" + }, + { + .name = BLOCK_OPT_BUFFER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "write buffer size" + }, { /* end of list */ } } }; diff --git a/include/block/block_int.h b/include/block/block_int.h index f4c75e8ba9..701f031102 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -61,6 +61,8 @@ #define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" #define BLOCK_OPT_COMPRESSION_TYPE "compression_type" #define BLOCK_OPT_EXTL2 "extended_l2" +#define BLOCK_OPT_CACHE "cache" +#define BLOCK_OPT_BUFFER_SIZE "buffer_size" #define BLOCK_PROBE_BUF_SIZE 512 diff --git a/qapi/block-core.json b/qapi/block-core.json index 804beabfb0..e65fabe36d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4437,6 +4437,8 @@ # @nocow: Turn off copy-on-write (valid only on btrfs; default: off) # @extent-size-hint: Extent size hint to add to the image file; 0 for not # adding an extent size hint (default: 1 MB, since 5.1) +# @cache: Cache mode used to write the output disk image +# @buffersize: Buffer size for creating image # # Since: 2.12 ## @@ -4445,7 +4447,9 @@ 'size': 'size', '*preallocation': 'PreallocMode', '*nocow': 'bool', - '*extent-size-hint': 'size'} } + '*extent-size-hint': 'size', + '*cache': 'str', + '*buffersize': 'size'} } ## # @BlockdevCreateOptionsGluster: diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 72bcdcfbfa..ec6aa2886a 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -52,9 +52,9 @@ SRST ERST DEF("create", img_create, - "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]") + "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-t cache] [-o options] filename [size]") SRST -.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE] +.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-t CACHE] [-o OPTIONS] FILENAME [SIZE] ERST DEF("dd", img_dd, diff --git a/qemu-img.c b/qemu-img.c index f036a1d428..9409558772 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -504,6 +504,7 @@ static int img_create(int argc, char **argv) const char *base_fmt = NULL; const char *filename; const char *base_filename = NULL; + const char *cache = BDRV_DEFAULT_CACHE; char *options = NULL; Error *local_err = NULL; bool quiet = false; @@ -515,7 +516,7 @@ static int img_create(int argc, char **argv) {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":F:b:f:ho:qu", + c = getopt_long(argc, argv, ":F:b:f:t:ho:qu", long_options, NULL); if (c == -1) { break; @@ -539,6 +540,9 @@ static int img_create(int argc, char **argv) case 'f': fmt = optarg; break; + case 't': + cache = optarg; + break; case 'o': if (accumulate_options(&options, optarg) < 0) { goto fail; @@ -582,6 +586,14 @@ static int img_create(int argc, char **argv) error_exit("Unexpected argument: %s", argv[optind]); } + if (!options) { + options = g_strdup_printf(BLOCK_OPT_CACHE"=%s", cache); + } else { + char *old_options = options; + options = g_strdup_printf("%s,"BLOCK_OPT_CACHE"=%s", options, cache); + g_free(old_options); + } + bdrv_img_create(filename, fmt, base_filename, base_fmt, options, img_size, flags, quiet, &local_err); if (local_err) { diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 8719c91b48..6aca1a7797 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -4,90 +4,90 @@ QA output created by 049 == 1. Traditional size parameter == qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16 cache=writeback == 2. Specifying size via -o == qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16 cache=writeback == 3. Invalid sizes == @@ -135,84 +135,84 @@ qemu-img: TEST_DIR/t.qcow2: The image size must be specified only once == Check correct interpretation of suffixes for cluster size == qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1048576 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1048576 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=524288 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=524288 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback == Check compat level option == qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img: TEST_DIR/t.qcow2: Parameter 'version' does not accept value '0.42' qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img: TEST_DIR/t.qcow2: Parameter 'version' does not accept value 'foobar' == Check preallocation option == qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=metadata compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=metadata compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img: TEST_DIR/t.qcow2: Parameter 'preallocation' does not accept value '1234' == Check encryption option == qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=off cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=off cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 --object secret,id=sec0,data=123456 -o encryption=on,encrypt.key-secret=sec0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=on encrypt.key-secret=sec0 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=on encrypt.key-secret=sec0 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 cache=writeback == Check lazy_refcounts option (only with v3) == qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=on refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=on refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16 cache=writeback qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=on refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=on refcount_bits=16 cache=writeback qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) == Expect error when backing file name is empty string == diff --git a/tests/qemu-iotests/099.out b/tests/qemu-iotests/099.out index 8cce627529..f6f8f25957 100644 --- a/tests/qemu-iotests/099.out +++ b/tests/qemu-iotests/099.out @@ -1,6 +1,6 @@ QA output created by 099 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 -Formatting 'TEST_DIR/t.IMGFMT.compare', fmt=raw size=131072 +Formatting 'TEST_DIR/t.IMGFMT.compare', fmt=raw size=131072 cache=writeback === Testing simple filename for blkverify === -- Gitee From 4b195103bc1e38c05eb67cd230051463b6dff03f Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Mon, 14 Feb 2022 14:42:05 +0800 Subject: [PATCH 183/486] log: Delete redudant qemu_log Delete redudant qemu_log in qmp_dispatch() Signed-off-by: Jingyi Wang --- qapi/qmp-dispatch.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 392ddb097c..e9ea5a70d4 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -222,20 +222,6 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, assert(!(oob && qemu_in_coroutine())); assert(monitor_cur() == NULL); - - json = qobject_to_json(QOBJECT(args)); - if (json) { - if ((strcmp(command, "query-block-jobs") != 0) - && (strcmp(command, "query-migrate") != 0) - && (strcmp(command, "query-blockstats") != 0) - && (strcmp(command, "query-balloon") != 0) - && (strcmp(command, "set_password") != 0)) { - qemu_log("qmp_cmd_name: %s, arguments: %s\n", - command, json->str); - } - g_string_free(json, true); - } - if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { monitor_set_cur(qemu_coroutine_self(), cur_mon); cmd->fn(args, &ret, &err); -- Gitee From 4f50ed900713acc14c971c07165fa83670d3f2b8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 13 Jan 2020 19:02:20 +0800 Subject: [PATCH 184/486] acpi/madt: Factor out the building of MADT GICC struct To realize CPU hotplug, the cpus aml within ACPI DSDT should contain _MAT mathod, which is equal to the GICC struct in ACPI MADT. Factor out the GICC building code from ACPI MADT and reuse it in build_cpus_aml. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt-acpi-build.c | 77 ++++++++++++++++++++++------------------ include/hw/arm/virt.h | 4 +++ 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 1ca705654b..64b1ed8672 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -771,6 +771,48 @@ static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size) build_append_int_noprefix(table_data, size, 4); /* Discovery Range Length */ } +void virt_madt_cpu_entry(AcpiDeviceIf *adev, int i, + const CPUArchIdList *possible_cpus, GArray *table_data, + bool force_enabled) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + const MemMapEntry *memmap = vms->memmap; + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); + uint64_t physical_base_address = 0, gich = 0, gicv = 0; + uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; + uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ? + PPI(VIRTUAL_PMU_IRQ) : 0; + + if (vms->gic_version == 2) { + physical_base_address = memmap[VIRT_GIC_CPU].base; + gicv = memmap[VIRT_GIC_VCPU].base; + gich = memmap[VIRT_GIC_HYP].base; + } + + /* 5.2.12.14 GIC Structure */ + build_append_int_noprefix(table_data, 0xB, 1); /* Type */ + build_append_int_noprefix(table_data, 76, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, i, 4); /* GIC ID */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ + /* Flags */ + build_append_int_noprefix(table_data, 1, 4); /* Enabled */ + /* Parking Protocol Version */ + build_append_int_noprefix(table_data, 0, 4); + /* Performance Interrupt GSIV */ + build_append_int_noprefix(table_data, pmu_interrupt, 4); + build_append_int_noprefix(table_data, 0, 8); /* Parked Address */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, physical_base_address, 8); + build_append_int_noprefix(table_data, gicv, 8); /* GICV */ + build_append_int_noprefix(table_data, gich, 8); /* GICH */ + /* VGIC Maintenance interrupt */ + build_append_int_noprefix(table_data, vgic_interrupt, 4); + build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ + /* MPIDR */ + build_append_int_noprefix(table_data, armcpu->mp_affinity, 8); +} + static void build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { @@ -798,40 +840,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 3); /* Reserved */ for (i = 0; i < MACHINE(vms)->smp.cpus; i++) { - ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); - uint64_t physical_base_address = 0, gich = 0, gicv = 0; - uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; - uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ? - PPI(VIRTUAL_PMU_IRQ) : 0; - - if (vms->gic_version == 2) { - physical_base_address = memmap[VIRT_GIC_CPU].base; - gicv = memmap[VIRT_GIC_VCPU].base; - gich = memmap[VIRT_GIC_HYP].base; - } - - /* 5.2.12.14 GIC Structure */ - build_append_int_noprefix(table_data, 0xB, 1); /* Type */ - build_append_int_noprefix(table_data, 76, 1); /* Length */ - build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - build_append_int_noprefix(table_data, i, 4); /* GIC ID */ - build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ - /* Flags */ - build_append_int_noprefix(table_data, 1, 4); /* Enabled */ - /* Parking Protocol Version */ - build_append_int_noprefix(table_data, 0, 4); - /* Performance Interrupt GSIV */ - build_append_int_noprefix(table_data, pmu_interrupt, 4); - build_append_int_noprefix(table_data, 0, 8); /* Parked Address */ - /* Physical Base Address */ - build_append_int_noprefix(table_data, physical_base_address, 8); - build_append_int_noprefix(table_data, gicv, 8); /* GICV */ - build_append_int_noprefix(table_data, gich, 8); /* GICH */ - /* VGIC Maintenance interrupt */ - build_append_int_noprefix(table_data, vgic_interrupt, 4); - build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ - /* MPIDR */ - build_append_int_noprefix(table_data, armcpu->mp_affinity, 8); + virt_madt_cpu_entry(NULL, i, NULL, table_data, false); } if (vms->gic_version == 3) { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index a4356cf736..36639e8d3e 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -38,6 +38,7 @@ #include "sysemu/kvm.h" #include "hw/intc/arm_gicv3_common.h" #include "qom/object.h" +#include "hw/acpi/acpi_dev_interface.h" #define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 @@ -181,6 +182,9 @@ OBJECT_DECLARE_TYPE(VirtMachineState, VirtMachineClass, VIRT_MACHINE) void virt_acpi_setup(VirtMachineState *vms); bool virt_is_acpi_enabled(VirtMachineState *vms); +void virt_madt_cpu_entry(AcpiDeviceIf *adev, int uid, + const CPUArchIdList *cpu_list, GArray *entry, + bool force_enabled); /* Return the number of used redistributor regions */ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) -- Gitee From ae74dda87e172ce82a8180d1a2e9c62904390f91 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 10:05:54 +0800 Subject: [PATCH 185/486] hw/arm/virt: Assign virt_madt_cpu_entry to acpi_ged madt_cpu hook In build_cpus_aml, we will invoke this hook to build _MAT aml mehtod for cpus. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 47e98f09e8..44c29070c4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -684,6 +684,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) static inline DeviceState *create_acpi_ged(VirtMachineState *vms) { DeviceState *dev; + AcpiDeviceIfClass *adevc; MachineState *ms = MACHINE(vms); int irq = vms->irqmap[VIRT_ACPI_GED]; uint32_t event = ACPI_GED_PWR_DOWN_EVT; @@ -699,6 +700,9 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); + adevc = ACPI_DEVICE_IF_GET_CLASS(dev); + adevc->madt_cpu = virt_madt_cpu_entry; + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq)); -- Gitee From 06837491e2ece2fdd6fe6cc8572aaab52fbdcb3e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 22 Apr 2020 15:58:27 +0800 Subject: [PATCH 186/486] arm/virt/acpi: Factor out CPPC building from DSDT CPU aml When CPU hotplug is enabled, we will use build_cpus_aml instead of acpi_dsdt_add_cpus, so factor out CPPC building and we can reuse it in build_cpus_aml. Signed-off-by: Keqian Zhu --- hw/arm/virt-acpi-build.c | 33 +++++++++++++++++----------- hw/arm/virt.c | 1 + include/hw/acpi/acpi_dev_interface.h | 2 ++ include/hw/arm/virt.h | 2 ++ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 64b1ed8672..a93d223879 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -120,8 +120,24 @@ static void acpi_dsdt_add_cppc(Aml *dev, uint64_t cpu_base, int *regs_offset) aml_append(dev, aml_name_decl("_CPC", cpc)); } -static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms, - const MemMapEntry *cppc_memmap) +void virt_acpi_dsdt_cpu_cppc(AcpiDeviceIf *adev, int ncpu, int num_cpu, Aml *dev) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + const MemMapEntry *cppc_memmap = &vms->memmap[VIRT_CPUFREQ]; + + /* + * Append _CPC and _PSD to support CPU frequence show + * Check CPPC available by DESIRED_PERF register + */ + if (cppc_regs_offset[DESIRED_PERF] != -1) { + acpi_dsdt_add_cppc(dev, + cppc_memmap->base + ncpu * CPPC_REG_PER_CPU_STRIDE, + cppc_regs_offset); + acpi_dsdt_add_psd(dev, num_cpu); + } +} + +static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms) { MachineState *ms = MACHINE(vms); uint16_t i; @@ -131,16 +147,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms, aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007"))); aml_append(dev, aml_name_decl("_UID", aml_int(i))); - /* - * Append _CPC and _PSD to support CPU frequence show - * Check CPPC available by DESIRED_PERF register - */ - if (cppc_regs_offset[DESIRED_PERF] != -1) { - acpi_dsdt_add_cppc(dev, - cppc_memmap->base + i * CPPC_REG_PER_CPU_STRIDE, - cppc_regs_offset); - acpi_dsdt_add_psd(dev, ms->smp.cpus); - } + virt_acpi_dsdt_cpu_cppc(NULL, i, ms->smp.cpus, dev); aml_append(scope, dev); } @@ -940,7 +947,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * the RTC ACPI device at all when using UEFI. */ scope = aml_scope("\\_SB"); - acpi_dsdt_add_cpus(scope, vms, &memmap[VIRT_CPUFREQ]); + acpi_dsdt_add_cpus(scope, vms); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); if (vmc->acpi_expose_flash) { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 44c29070c4..3299d674c8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -702,6 +702,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) adevc = ACPI_DEVICE_IF_GET_CLASS(dev); adevc->madt_cpu = virt_madt_cpu_entry; + adevc->cpu_cppc = virt_acpi_dsdt_cpu_cppc; sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index ea6056ab92..601931433a 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -5,6 +5,7 @@ #include "qom/object.h" #include "hw/boards.h" #include "hw/qdev-core.h" +#include "hw/acpi/aml-build.h" /* These values are part of guest ABI, and can not be changed */ typedef enum { @@ -55,5 +56,6 @@ struct AcpiDeviceIfClass { void (*madt_cpu)(AcpiDeviceIf *adev, int uid, const CPUArchIdList *apic_ids, GArray *entry, bool force_enabled); + void (*cpu_cppc)(AcpiDeviceIf *adev, int uid, int num_cpu, Aml *dev); }; #endif diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 36639e8d3e..fe26709e1a 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -185,6 +185,8 @@ bool virt_is_acpi_enabled(VirtMachineState *vms); void virt_madt_cpu_entry(AcpiDeviceIf *adev, int uid, const CPUArchIdList *cpu_list, GArray *entry, bool force_enabled); +void virt_acpi_dsdt_cpu_cppc(AcpiDeviceIf *adev, int uid, + int num_cpu, Aml *dev); /* Return the number of used redistributor regions */ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) -- Gitee From 1ab75151c0a486ebdbe50d29c9677c7bc1f05929 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 22 Apr 2020 16:11:13 +0800 Subject: [PATCH 187/486] acpi/cpu: Prepare build_cpus_aml for arm virt We will reuse build_cpus_aml to build DSDT cpus aml in arm/virt ACPI to realize cpu hotplug. Three points are added. 1. Make ACPI IO address space configurable, because ARM64 platforms don't use port IO for ACPI IO space. 2. Add GICC struct building support in _MAT of cpu aml. 3. Let the hotplug method parameter can be NULL, because ACPI GED will realize it. Besides, CPU CPPC building is injected. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/acpi/cpu.c | 27 ++++++++++++++++++++------- hw/i386/acpi-build.c | 2 +- include/hw/acpi/cpu.h | 3 ++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index b20903ea30..a9c2ee952a 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -343,7 +343,8 @@ const VMStateDescription vmstate_cpu_hotplug = { void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, const char *res_root, - const char *event_handler_method) + const char *event_handler_method, + AmlRegionSpace rs) { Aml *ifctx; Aml *field; @@ -371,13 +372,18 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, - ACPI_CPU_HOTPLUG_REG_LEN)); + if (rs == AML_SYSTEM_IO) { + aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, + ACPI_CPU_HOTPLUG_REG_LEN)); + } else { + aml_append(crs, aml_memory32_fixed(io_base, + ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE)); + } aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); /* declare CPU hotplug MMIO region with related access fields */ aml_append(cpu_ctrl_dev, - aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base), + aml_operation_region("PRST", rs, aml_int(io_base), ACPI_CPU_HOTPLUG_REG_LEN)); field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, @@ -663,6 +669,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(dev, aml_name_decl("_UID", uid)); } + assert(adevc); + if (adevc->cpu_cppc) { + adevc->cpu_cppc(adev, i, arch_ids->len, dev); + } + method = aml_method("_STA", 0, AML_SERIALIZED); aml_append(method, aml_return(aml_call1(CPU_STS_METHOD, uid))); aml_append(dev, method); @@ -703,9 +714,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(sb_scope, cpus_dev); aml_append(table, sb_scope); - method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); - aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD)); - aml_append(table, method); + if (event_handler_method) { + method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD)); + aml_append(table, method); + } g_free(cphp_res_path); } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index a99c6e4fe3..1ce2d67c2e 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1513,7 +1513,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, .fw_unplugs_cpu = pm->smi_on_cpu_unplug, }; build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, - "\\_SB.PCI0", "\\_GPE._E02"); + "\\_SB.PCI0", "\\_GPE._E02", AML_SYSTEM_IO); } if (pcms->memhp_io_base && nr_mem) { diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 999caaf510..a0fdc44bdd 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -58,7 +58,8 @@ typedef struct CPUHotplugFeatures { void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, const char *res_root, - const char *event_handler_method); + const char *event_handler_method, + AmlRegionSpace rs); void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list); -- Gitee From 603cbcc5efdd35f518a5bd0a5067d40c2c4eb8d6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 3 Apr 2020 15:41:01 +0800 Subject: [PATCH 188/486] acpi/ged: Extend ACPI GED to support CPU hotplug This adds a new GED event called ACPI_GED_CPU_HOTPLUG_EVT. The basic workflow is that: GED sends this event to guest, then ACPI driver in guest will call _EVT method of GED aml, then _EVT will call CSCN method in cpus aml to get status of all cpus. The status of cpus is maintained by CPUHotplugState in GED and is made accessable to guest through memory region. This also adds migration support to CPUHotplugState. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- docs/specs/acpi_hw_reduced_hotplug.rst | 3 ++- hw/acpi/cpu.c | 1 - hw/acpi/generic_event_device.c | 35 ++++++++++++++++++++++++++ hw/arm/Kconfig | 1 + include/hw/acpi/cpu.h | 2 ++ include/hw/acpi/generic_event_device.h | 4 +++ 6 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst index 0bd3f9399f..3acd6fcd8b 100644 --- a/docs/specs/acpi_hw_reduced_hotplug.rst +++ b/docs/specs/acpi_hw_reduced_hotplug.rst @@ -64,7 +64,8 @@ GED IO interface (4 byte access) 0: Memory hotplug event 1: System power down event 2: NVDIMM hotplug event - 3-31: Reserved + 3: CPU hotplug event + 4-31: Reserved **write_access:** diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index a9c2ee952a..f9ce0a7f41 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -6,7 +6,6 @@ #include "trace.h" #include "sysemu/numa.h" -#define ACPI_CPU_HOTPLUG_REG_LEN 12 #define ACPI_CPU_SELECTOR_OFFSET_WR 0 #define ACPI_CPU_FLAGS_OFFSET_RW 4 #define ACPI_CPU_CMD_OFFSET_WR 5 diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index e28457a7d1..042a8ef8a5 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -25,6 +25,7 @@ static const uint32_t ged_supported_events[] = { ACPI_GED_MEM_HOTPLUG_EVT, ACPI_GED_PWR_DOWN_EVT, ACPI_GED_NVDIMM_HOTPLUG_EVT, + ACPI_GED_CPU_HOTPLUG_EVT, }; /* @@ -117,6 +118,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_notify(aml_name("\\_SB.NVDR"), aml_int(0x80))); break; + case ACPI_GED_CPU_HOTPLUG_EVT: + aml_append(if_ctx, aml_call0("\\_SB.CPUS.CSCN")); + break; default: /* * Please make sure all the events in ged_supported_events[] @@ -234,6 +238,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } else { acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -279,6 +285,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) sel = ACPI_GED_PWR_DOWN_EVT; } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) { sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; + } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { + sel = ACPI_GED_CPU_HOTPLUG_EVT; } else { /* Unknown event. Return without generating interrupt. */ warn_report("GED: Unsupported event %d. No irq injected", ev); @@ -311,6 +319,16 @@ static const VMStateDescription vmstate_memhp_state = { } }; +static const VMStateDescription vmstate_cpuhp_state = { + .name = "acpi-ged/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, AcpiGedState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ged_state = { .name = "acpi-ged-state", .version_id = 1, @@ -360,6 +378,7 @@ static const VMStateDescription vmstate_acpi_ged = { .subsections = (const VMStateDescription * []) { &vmstate_memhp_state, &vmstate_ghes_state, + &vmstate_cpuhp_state, NULL } }; @@ -370,6 +389,7 @@ static void acpi_ged_initfn(Object *obj) AcpiGedState *s = ACPI_GED(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); GEDState *ged_st = &s->ged_state; + MachineClass *mc; memory_region_init_io(&ged_st->evt, obj, &ged_evt_ops, ged_st, TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN); @@ -393,6 +413,21 @@ static void acpi_ged_initfn(Object *obj) memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st, TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT); sysbus_init_mmio(sbd, &ged_st->regs); + + mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (!mc->possible_cpu_arch_ids) { + /* + * MachineClass should support possible_cpu_arch_ids in + * cpu_hotplug_hw_init below. + */ + return; + } + + memory_region_init(&s->container_cpuhp, OBJECT(dev), "cpuhp container", + ACPI_CPU_HOTPLUG_REG_LEN); + sysbus_init_mmio(sbd, &s->container_cpuhp); + cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), + &s->cpuhp_state, 0); } static void acpi_ged_class_init(ObjectClass *class, void *data) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2d37d29f02..006a4b4c4b 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -27,6 +27,7 @@ config ARM_VIRT select DIMM select ACPI_HW_REDUCED select ACPI_APEI + select ACPI_CPU_HOTPLUG config CHEETAH bool diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index a0fdc44bdd..d521025830 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -17,6 +17,8 @@ #include "hw/acpi/aml-build.h" #include "hw/hotplug.h" +#define ACPI_CPU_HOTPLUG_REG_LEN 12 + typedef struct AcpiCpuStatus { struct CPUState *cpu; uint64_t arch_id; diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d49217c445..6bb2ade385 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -63,6 +63,7 @@ #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/ghes.h" #include "qom/object.h" +#include "hw/acpi/cpu.h" #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -97,6 +98,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define ACPI_GED_MEM_HOTPLUG_EVT 0x1 #define ACPI_GED_PWR_DOWN_EVT 0x2 #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 +#define ACPI_GED_CPU_HOTPLUG_EVT 0x8 typedef struct GEDState { MemoryRegion evt; @@ -108,6 +110,8 @@ struct AcpiGedState { SysBusDevice parent_obj; MemHotplugState memhp_state; MemoryRegion container_memhp; + CPUHotplugState cpuhp_state; + MemoryRegion container_cpuhp; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; -- Gitee From 42072fd4b33125959d825a0c5cee0d1122072f71 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 10:17:27 +0800 Subject: [PATCH 189/486] arm/cpu: assign arm_get_arch_id handler to get_arch_id hook This hook will be called in get_cpu_status, which is called during cpu hotplug. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- target/arm/cpu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 65163f5135..f06ba29885 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2557,6 +2557,13 @@ static const struct TCGCPUOps arm_tcg_ops = { }; #endif /* CONFIG_TCG */ +static int64_t arm_cpu_get_arch_id(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + + return cpu->mp_affinity; +} + static void arm_cpu_class_init(ObjectClass *oc, void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); @@ -2575,6 +2582,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->set_pc = arm_cpu_set_pc; cc->gdb_read_register = arm_cpu_gdb_read_register; cc->gdb_write_register = arm_cpu_gdb_write_register; + cc->get_arch_id = arm_cpu_get_arch_id; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &arm_sysemu_ops; #endif -- Gitee From d215714b9ab38f6c9e0aacf2120b44888936a1ed Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 11 Feb 2022 15:48:16 +0800 Subject: [PATCH 190/486] tests/acpi/bios-tables-test: Allow changes to virt/DSDT file Let virt/DSDT as the expected file allowed to be changed. Signed-off-by: Keqian Zhu --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..7b4adbc822 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/DSDT", -- Gitee From 6b0f94aee82c7558d79e5ec28437483c4873dc65 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 5 Apr 2020 16:03:15 +0800 Subject: [PATCH 191/486] arm/virt: Attach ACPI CPU hotplug support to virt Attach cpus aml building and GED support for CPU hotplug to arm/virt, but currently we make it diabled by not add CPU hotplug event to GED. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt-acpi-build.c | 15 ++++++++++++++- hw/arm/virt.c | 6 ++++++ include/hw/arm/virt.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index a93d223879..7cb320d9f2 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -937,6 +937,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const int *irqmap = vms->irqmap; AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + bool cpu_aml_built = false; acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); @@ -947,7 +948,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * the RTC ACPI device at all when using UEFI. */ scope = aml_scope("\\_SB"); - acpi_dsdt_add_cpus(scope, vms); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); if (vmc->acpi_expose_flash) { @@ -977,6 +977,19 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) AML_SYSTEM_MEMORY, memmap[VIRT_PCDIMM_ACPI].base); } + + if (event & ACPI_GED_CPU_HOTPLUG_EVT) { + CPUHotplugFeatures opts = { + .acpi_1_compatible = false, .has_legacy_cphp = false + }; + build_cpus_aml(dsdt, ms, opts, memmap[VIRT_CPU_ACPI].base, + "\\_SB", NULL, AML_SYSTEM_MEMORY); + cpu_aml_built = true; + } + } + + if (!cpu_aml_built) { + acpi_dsdt_add_cpus(scope, vms); } acpi_dsdt_add_power_button(scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3299d674c8..9b73c479c4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -154,6 +154,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, + [VIRT_CPU_ACPI] = { 0x090c0000, ACPI_CPU_HOTPLUG_REG_LEN }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, [VIRT_CPUFREQ] = { 0x0b000000, 0x00010000 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ @@ -697,6 +698,10 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) event |= ACPI_GED_NVDIMM_HOTPLUG_EVT; } + /* event |= ACPI_GED_CPU_HOTPLUG_EVT; + * Currently CPU hotplug is not enabled. + */ + dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); @@ -706,6 +711,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, vms->memmap[VIRT_CPU_ACPI].base); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index fe26709e1a..2a838620d8 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -88,6 +88,7 @@ enum { VIRT_ACPI_GED, VIRT_NVDIMM_ACPI, VIRT_PVTIME, + VIRT_CPU_ACPI, VIRT_LOWMEMMAP_LAST, }; -- Gitee From d3d158cbf6b236022794e867ac395f75fb4f6436 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 11 Feb 2022 16:07:31 +0800 Subject: [PATCH 192/486] tests/acpi/bios-table-test: Update expected virt/DSDT file Update DSDT binary and empty bios-tables-test-allowd-diff.h Signed-off-by: Keqian Zhu --- tests/data/acpi/virt/DSDT | Bin 5669 -> 5669 bytes tests/data/acpi/virt/DSDT.memhp | Bin 7030 -> 7030 bytes tests/data/acpi/virt/DSDT.numamem | Bin 5669 -> 5669 bytes tests/data/acpi/virt/DSDT.pxb | Bin 8152 -> 8152 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 5 files changed, 1 deletion(-) diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT index dd8573f0312918ea1ba17496e9f3a275e98ab214..4643f6fa4510fade07c1187d230b92b49cd28cba 100644 GIT binary patch delta 19 bcmZ3gvs7n-{$>@X7s4C$zcNnd5t9S}Oacbl delta 19 bcmZ3gvs7n-{$_EJuZ$b@UkFd;5t9S}Oc(~= diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp index d764481440adea59d928a1a48afe0ba1ce135597..f03b87702e3ec04b8308a1843aa373174c2ed7bb 100644 GIT binary patch delta 19 bcmexn_RVa9{$>>>OUaG;Ul}LIN=pI&Qx^w4 delta 19 bcmexn_RVa9{^ktHuZ$b@EhQ(%N=pI&S=0xZ diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem index dd8573f0312918ea1ba17496e9f3a275e98ab214..4643f6fa4510fade07c1187d230b92b49cd28cba 100644 GIT binary patch delta 19 bcmZ3gvs7n-{$>@X7s4C$zcNnd5t9S}Oacbl delta 19 bcmZ3gvs7n-{$_EJuZ$b@UkFd;5t9S}Oc(~= diff --git a/tests/data/acpi/virt/DSDT.pxb b/tests/data/acpi/virt/DSDT.pxb index 9ff22b5ea465d2f678beb2ce9d905861d69a5b87..8dd662f4bacfa6cb73d454932710f1a3d1d7a33f 100644 GIT binary patch delta 19 bcmca%f5U!){$>@X*|Hn;zcNnVFE0rIRW}FU delta 19 bcmca%f5U!){^nD%Ul}**&z7CMUtSUbUmyse diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 7b4adbc822..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/virt/DSDT", -- Gitee From 209b3e4e522b8f7f41e495feaade96ee9a91e46a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 3 Apr 2020 16:16:18 +0800 Subject: [PATCH 193/486] arm/virt: Add CPU hotplug framework Establish the CPU hotplug framework for arm/virt, we will add necessary code legs to this framework gradually to realize CPU hotplug finally. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9b73c479c4..11155fcb70 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2586,6 +2586,18 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, dev, &error_abort); } +static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + /* Currently nothing to do */ +} + +static void virt_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + /* Currently nothing to do */ +} + static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -2619,6 +2631,8 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, qdev_prop_set_uint32(dev, "len-reserved-regions", 1); qdev_prop_set_string(dev, "reserved-regions[0]", resv_prop_str); g_free(resv_prop_str); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + virt_cpu_pre_plug(hotplug_dev, dev, errp); } } @@ -2637,6 +2651,8 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, } if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_memory_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + virt_cpu_plug(hotplug_dev, dev, errp); } if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -2717,7 +2733,8 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || - (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) { + (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) || + (object_dynamic_cast(OBJECT(dev), TYPE_CPU))) { return HOTPLUG_HANDLER(machine); } if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { -- Gitee From 5454c00908236dcabcbf9ae246ccb69e1fcea72e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 6 Apr 2020 10:54:35 +0800 Subject: [PATCH 194/486] arm/virt: Add CPU topology support The CPU topology specified by user (through -smp options) is used in ACPI PPTT. Now we will use this information to locate which CPU to plug or unplug. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 87 ++++++++++++++++++++++++++++++++++++++- include/hw/arm/topology.h | 68 ++++++++++++++++++++++++++++++ qapi/machine.json | 2 + target/arm/cpu.c | 4 ++ target/arm/cpu.h | 4 ++ 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 include/hw/arm/topology.h diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 11155fcb70..a12e718686 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -39,6 +39,7 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/arm/primecell.h" +#include "hw/arm/topology.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" #include "hw/vfio/vfio-calxeda-xgmac.h" @@ -2524,6 +2525,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) int n; unsigned int max_cpus = ms->smp.max_cpus; VirtMachineState *vms = VIRT_MACHINE(ms); + ARMCPUTopoInfo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -2535,10 +2537,19 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { ms->possible_cpus->cpus[n].type = ms->cpu_type; + ms->possible_cpus->cpus[n].vcpus_count = 1; ms->possible_cpus->cpus[n].arch_id = virt_cpu_mp_affinity(vms, n); + + topo_ids_from_idx(n, ms->smp.clusters, ms->smp.cores, ms->smp.threads, &topo); + ms->possible_cpus->cpus[n].props.has_socket_id = true; + ms->possible_cpus->cpus[n].props.socket_id = topo.pkg_id; + ms->possible_cpus->cpus[n].props.has_cluster_id = true; + ms->possible_cpus->cpus[n].props.cluster_id = topo.cluster_id; + ms->possible_cpus->cpus[n].props.has_core_id = true; + ms->possible_cpus->cpus[n].props.core_id = topo.core_id; ms->possible_cpus->cpus[n].props.has_thread_id = true; - ms->possible_cpus->cpus[n].props.thread_id = n; + ms->possible_cpus->cpus[n].props.thread_id = topo.smt_id; } return ms->possible_cpus; } @@ -2589,7 +2600,79 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - /* Currently nothing to do */ + CPUState *cs = CPU(dev); + ARMCPUTopoInfo topo; + ARMCPU *cpu = ARM_CPU(dev); + MachineState *ms = MACHINE(hotplug_dev); + int smp_clusters = ms->smp.clusters; + int smp_cores = ms->smp.cores; + int smp_threads = ms->smp.threads; + + /* if cpu idx is not set, set it based on socket/cluster/core/thread + * properties + */ + if (cs->cpu_index == UNASSIGNED_CPU_INDEX) { + int max_socket = ms->smp.max_cpus / smp_threads / smp_cores / smp_clusters; + if (cpu->socket_id < 0 || cpu->socket_id >= max_socket) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, max_socket - 1); + return; + } + if (cpu->cluster_id < 0 || cpu->cluster_id >= smp_clusters) { + error_setg(errp, "Invalid CPU cluster-id: %u must be in range 0:%u", + cpu->cluster_id, smp_clusters - 1); + return; + } + if (cpu->core_id < 0 || cpu->core_id >= smp_cores) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0 || cpu->thread_id >= smp_threads) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo.pkg_id = cpu->socket_id; + topo.cluster_id = cpu->cluster_id; + topo.core_id = cpu->core_id; + topo.smt_id = cpu->thread_id; + cs->cpu_index = idx_from_topo_ids(smp_clusters, smp_cores, smp_threads, &topo); + } + + /* if 'address' properties socket-id/cluster-id/core-id/thread-id are not + * set, set them so that machine_query_hotpluggable_cpus would show correct + * values + */ + topo_ids_from_idx(cs->cpu_index, smp_clusters, smp_cores, smp_threads, &topo); + if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set idx:" + " 0x%x (socket-id: %u)", cpu->socket_id, cs->cpu_index, topo.pkg_id); + return; + } + cpu->socket_id = topo.pkg_id; + + if (cpu->cluster_id != -1 && cpu->cluster_id != topo.cluster_id) { + error_setg(errp, "property cluster-id: %u doesn't match set idx:" + " 0x%x (cluster-id: %u)", cpu->cluster_id, cs->cpu_index, topo.cluster_id); + return; + } + cpu->cluster_id = topo.cluster_id; + + if (cpu->core_id != -1 && cpu->core_id != topo.core_id) { + error_setg(errp, "property core-id: %u doesn't match set idx:" + " 0x%x (core-id: %u)", cpu->core_id, cs->cpu_index, topo.core_id); + return; + } + cpu->core_id = topo.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set idx:" + " 0x%x (thread-id: %u)", cpu->thread_id, cs->cpu_index, topo.smt_id); + return; + } + cpu->thread_id = topo.smt_id; } static void virt_cpu_plug(HotplugHandler *hotplug_dev, diff --git a/include/hw/arm/topology.h b/include/hw/arm/topology.h new file mode 100644 index 0000000000..d0dad1a9a3 --- /dev/null +++ b/include/hw/arm/topology.h @@ -0,0 +1,68 @@ +/* + * ARM CPU topology data structures and functions + * + * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO.,LTD. + * + * 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 . + */ + +#ifndef HW_ARM_TOPOLOGY_H +#define HW_ARM_TOPOLOGY_H + +typedef struct ARMCPUTopoInfo { + unsigned pkg_id; + unsigned cluster_id; + unsigned core_id; + unsigned smt_id; +} ARMCPUTopoInfo; + +/* Calculate (contiguous) CPU index based on topology */ +static inline unsigned idx_from_topo_ids(unsigned nr_clusters, + unsigned nr_cores, + unsigned nr_threads, + const ARMCPUTopoInfo *topo) +{ + assert(nr_clusters > 0); + assert(nr_cores > 0); + assert(nr_threads > 0); + assert(topo != NULL); + + return topo->pkg_id * nr_clusters * nr_cores * nr_threads + + topo->cluster_id * nr_cores + + topo->core_id * nr_threads + + topo->smt_id; +} + +/* Calculate thread/core/cluster/package topology + * based on (contiguous) CPU index + */ +static inline void topo_ids_from_idx(unsigned cpu_index, + unsigned nr_clusters, + unsigned nr_cores, + unsigned nr_threads, + ARMCPUTopoInfo *topo) +{ + assert(nr_clusters > 0); + assert(nr_cores > 0); + assert(nr_threads > 0); + assert(topo != NULL); + + topo->smt_id = cpu_index % nr_threads; + topo->core_id = cpu_index / nr_threads % nr_cores; + topo->cluster_id = cpu_index / nr_threads / nr_cores % nr_clusters; + topo->pkg_id = cpu_index / nr_threads / nr_cores / nr_clusters; +} + +#endif /* HW_ARM_TOPOLOGY_H */ + diff --git a/qapi/machine.json b/qapi/machine.json index 8faa51074e..6822cafe2e 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -868,6 +868,7 @@ # @node-id: NUMA node ID the CPU belongs to # @socket-id: socket number within node/board the CPU belongs to # @die-id: die number within socket the CPU belongs to (since 4.1) +# @cluster-id: cluster number within die the CPU belongs to (since 6.2) # @core-id: core number within die the CPU belongs to # @thread-id: thread number within core the CPU belongs to # @@ -883,6 +884,7 @@ 'data': { '*node-id': 'int', '*socket-id': 'int', '*die-id': 'int', + '*cluster-id': 'int', '*core-id': 'int', '*thread-id': 'int' } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f06ba29885..9fd8e57971 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2507,6 +2507,10 @@ static Property arm_cpu_properties[] = { DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, -1), + DEFINE_PROP_INT32("cluster-id", ARMCPU, cluster_id, -1), + DEFINE_PROP_INT32("core-id", ARMCPU, core_id, -1), + DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, -1), DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), DEFINE_PROP_END_OF_LIST() }; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 947897d5ac..eb804dffaa 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1006,6 +1006,10 @@ struct ARMCPU { QLIST_HEAD(, ARMELChangeHook) el_change_hooks; int32_t node_id; /* NUMA node this CPU belongs to */ + int32_t socket_id; + int32_t cluster_id; + int32_t core_id; + int32_t thread_id; /* Used to synchronize KVM and QEMU in-kernel device levels */ uint8_t device_irq_level; -- Gitee From 1a347bf3f8e7e31cdaed265af22853d66c202090 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 23 Apr 2020 20:54:18 +0800 Subject: [PATCH 195/486] test/numa: Adjust aarch64 numa test We have supported topology for arm/virt in previous patch, which changes the meaning of "thread-id", so we must modify test case. Signed-off-by: Keqian Zhu --- tests/qtest/numa-test.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 90bf68a5b3..08f28012c8 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -223,17 +223,17 @@ static void aarch64_numa_cpu(const void *data) QTestState *qts; g_autofree char *cli = NULL; - cli = make_cli(data, "-machine smp.cpus=2 " + cli = make_cli(data, "-machine smp.cpus=2,smp.cores=2 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " - "-numa cpu,node-id=1,thread-id=0 " - "-numa cpu,node-id=0,thread-id=1"); + "-numa cpu,node-id=1,core-id=0 " + "-numa cpu,node-id=0,core-id=1"); qts = qtest_init(cli); cpus = get_cpus(qts, &resp); g_assert(cpus); while ((e = qlist_pop(cpus))) { QDict *cpu, *props; - int64_t thread, node; + int64_t core, node; cpu = qobject_to(QDict, e); g_assert(qdict_haskey(cpu, "props")); @@ -241,12 +241,12 @@ static void aarch64_numa_cpu(const void *data) g_assert(qdict_haskey(props, "node-id")); node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "thread-id")); - thread = qdict_get_int(props, "thread-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); - if (thread == 0) { + if (core == 0) { g_assert_cmpint(node, ==, 1); - } else if (thread == 1) { + } else if (core == 1) { g_assert_cmpint(node, ==, 0); } else { g_assert(false); -- Gitee From 7838609e9a7743af03c58ae46afd26070b2feea6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 6 Apr 2020 12:50:54 +0800 Subject: [PATCH 196/486] hw/arm/virt: Factor out some CPU init codes to pre_plug hook The init path of hotplugged CPU is pre_plug/realize/plug, so we must move these init code in machvirt_init to pre_plug hook, to let them be shared by all CPUs. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 197 ++++++++++++++++++++++++++------------------------ 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a12e718686..149e0245d7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -213,6 +213,10 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("max"), }; +static MemoryRegion *secure_sysmem; +static MemoryRegion *tag_sysmem; +static MemoryRegion *secure_tag_sysmem; + static bool cpu_type_valid(const char *cpu) { int i; @@ -1990,9 +1994,6 @@ static void machvirt_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *possible_cpus; MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *secure_sysmem = NULL; - MemoryRegion *tag_sysmem = NULL; - MemoryRegion *secure_tag_sysmem = NULL; int n, virt_max_cpus; bool firmware_loaded; bool aarch64 = true; @@ -2099,100 +2100,11 @@ static void machvirt_init(MachineState *machine) } cpuobj = object_new(possible_cpus->cpus[n].type); - object_property_set_int(cpuobj, "mp-affinity", - possible_cpus->cpus[n].arch_id, NULL); + aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); cs = CPU(cpuobj); cs->cpu_index = n; - numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), - &error_fatal); - - aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); - - if (!vms->secure) { - object_property_set_bool(cpuobj, "has_el3", false, NULL); - } - - if (!vms->virt && object_property_find(cpuobj, "has_el2")) { - object_property_set_bool(cpuobj, "has_el2", false, NULL); - } - - if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { - object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit, - NULL); - - /* Secondary CPUs start in PSCI powered-down state */ - if (n > 0) { - object_property_set_bool(cpuobj, "start-powered-off", true, - NULL); - } - } - - if (vmc->kvm_no_adjvtime && - object_property_find(cpuobj, "kvm-no-adjvtime")) { - object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL); - } - - if (vmc->no_kvm_steal_time && - object_property_find(cpuobj, "kvm-steal-time")) { - object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL); - } - - if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) { - object_property_set_bool(cpuobj, "pmu", false, NULL); - } - - if (object_property_find(cpuobj, "reset-cbar")) { - object_property_set_int(cpuobj, "reset-cbar", - vms->memmap[VIRT_CPUPERIPHS].base, - &error_abort); - } - - object_property_set_link(cpuobj, "memory", OBJECT(sysmem), - &error_abort); - if (vms->secure) { - object_property_set_link(cpuobj, "secure-memory", - OBJECT(secure_sysmem), &error_abort); - } - - if (vms->mte) { - /* Create the memory region only once, but link to all cpus. */ - if (!tag_sysmem) { - /* - * The property exists only if MemTag is supported. - * If it is, we must allocate the ram to back that up. - */ - if (!object_property_find(cpuobj, "tag-memory")) { - error_report("MTE requested, but not supported " - "by the guest CPU"); - exit(1); - } - - tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(tag_sysmem, OBJECT(machine), - "tag-memory", UINT64_MAX / 32); - - if (vms->secure) { - secure_tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(secure_tag_sysmem, OBJECT(machine), - "secure-tag-memory", UINT64_MAX / 32); - - /* As with ram, secure-tag takes precedence over tag. */ - memory_region_add_subregion_overlap(secure_tag_sysmem, 0, - tag_sysmem, -1); - } - } - - object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem), - &error_abort); - if (vms->secure) { - object_property_set_link(cpuobj, "secure-tag-memory", - OBJECT(secure_tag_sysmem), - &error_abort); - } - } - qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); object_unref(cpuobj); } @@ -2600,10 +2512,16 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - CPUState *cs = CPU(dev); ARMCPUTopoInfo topo; + Object *cpuobj = OBJECT(dev); + CPUState *cs = CPU(dev); ARMCPU *cpu = ARM_CPU(dev); MachineState *ms = MACHINE(hotplug_dev); + MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev); + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(hotplug_dev); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + MemoryRegion *sysmem = get_system_memory(); int smp_clusters = ms->smp.clusters; int smp_cores = ms->smp.cores; int smp_threads = ms->smp.threads; @@ -2673,6 +2591,97 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } cpu->thread_id = topo.smt_id; + + /* Init some properties */ + + object_property_set_int(cpuobj, "mp-affinity", + possible_cpus->cpus[cs->cpu_index].arch_id, NULL); + + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), + &error_fatal); + + if (!vms->secure) { + object_property_set_bool(cpuobj, "has_el3", false, NULL); + } + + if (!vms->virt && object_property_find(cpuobj, "has_el2")) { + object_property_set_bool(cpuobj, "has_el2", false, NULL); + } + + if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { + object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit, + NULL); + + /* Secondary CPUs start in PSCI powered-down state */ + if (cs->cpu_index > 0) { + object_property_set_bool(cpuobj, "start-powered-off", true, + NULL); + } + } + + if (vmc->kvm_no_adjvtime && + object_property_find(cpuobj, "kvm-no-adjvtime")) { + object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL); + } + + if (vmc->no_kvm_steal_time && + object_property_find(cpuobj, "kvm-steal-time")) { + object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL); + } + + if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) { + object_property_set_bool(cpuobj, "pmu", false, NULL); + } + + if (object_property_find(cpuobj, "reset-cbar")) { + object_property_set_int(cpuobj, "reset-cbar", + vms->memmap[VIRT_CPUPERIPHS].base, + &error_abort); + } + + object_property_set_link(cpuobj, "memory", OBJECT(sysmem), + &error_abort); + if (vms->secure) { + object_property_set_link(cpuobj, "secure-memory", + OBJECT(secure_sysmem), &error_abort); + } + + if (vms->mte) { + /* Create the memory region only once, but link to all cpus. */ + if (!tag_sysmem) { + /* + * The property exists only if MemTag is supported. + * If it is, we must allocate the ram to back that up. + */ + if (!object_property_find(cpuobj, "tag-memory")) { + error_report("MTE requested, but not supported " + "by the guest CPU"); + exit(1); + } + + tag_sysmem = g_new(MemoryRegion, 1); + memory_region_init(tag_sysmem, OBJECT(ms), + "tag-memory", UINT64_MAX / 32); + + if (vms->secure) { + secure_tag_sysmem = g_new(MemoryRegion, 1); + memory_region_init(secure_tag_sysmem, OBJECT(ms), + "secure-tag-memory", UINT64_MAX / 32); + + /* As with ram, secure-tag takes precedence over tag. */ + memory_region_add_subregion_overlap(secure_tag_sysmem, 0, + tag_sysmem, -1); + } + } + + object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem), + &error_abort); + if (vms->secure) { + object_property_set_link(cpuobj, "secure-tag-memory", + OBJECT(secure_tag_sysmem), + &error_abort); + } + } } static void virt_cpu_plug(HotplugHandler *hotplug_dev, -- Gitee From 9dc22ff87eb61a8b2bcc5892961ec432986893c9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 9 Apr 2020 09:31:22 +0800 Subject: [PATCH 197/486] hw/arm/boot: Add manually register and trigger of CPU reset We need to register and trigger CPU reset manually for hotplugged CPU. Besides, we gather CPU reset handlers of all CPUs because CPU reset should happen before GIC reset. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/boot.c | 18 ++++++++++++++++++ hw/core/reset.c | 25 +++++++++++++++++++++++++ include/hw/arm/boot.h | 3 +++ include/sysemu/reset.h | 4 ++++ 4 files changed, 50 insertions(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 21024f7999..3d45de1772 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -814,6 +814,24 @@ static void do_cpu_reset(void *opaque) } } +void cpu_hotplug_register_reset(int ncpu) +{ + CPUState *cpu_0 = qemu_get_cpu(0); + CPUState *cpu = qemu_get_cpu(ncpu); + QEMUResetEntry *entry = qemu_get_reset_entry(do_cpu_reset, cpu_0); + + assert(entry); + /* Gather the reset handlers of all CPUs */ + qemu_register_reset_after(entry, do_cpu_reset, cpu); +} + +void cpu_hotplug_reset_manually(int ncpu) +{ + CPUState *cpu = qemu_get_cpu(ncpu); + + do_cpu_reset(cpu); +} + /** * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified * by key. diff --git a/hw/core/reset.c b/hw/core/reset.c index e923723d38..314d332111 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -48,6 +48,31 @@ void qemu_register_reset(QEMUResetHandler *func, void *opaque) QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); } +QEMUResetEntry *qemu_get_reset_entry(QEMUResetHandler *func, + void *opaque) +{ + QEMUResetEntry *re; + + QTAILQ_FOREACH(re, &reset_handlers, entry) { + if (re->func == func && re->opaque == opaque) { + return re; + } + } + + return NULL; +} + +void qemu_register_reset_after(QEMUResetEntry *entry, + QEMUResetHandler *func, + void *opaque) +{ + QEMUResetEntry *re = g_malloc0(sizeof(QEMUResetEntry)); + + re->func = func; + re->opaque = opaque; + QTAILQ_INSERT_AFTER(&reset_handlers, entry, re, entry); +} + void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) { QEMUResetEntry *re; diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index ce2b48b88b..c3c4d3ea79 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -119,6 +119,9 @@ struct arm_boot_info { arm_endianness endianness; }; +void cpu_hotplug_register_reset(int ncpu); +void cpu_hotplug_reset_manually(int ncpu); + /** * arm_load_kernel - Loads memory with everything needed to boot * diff --git a/include/sysemu/reset.h b/include/sysemu/reset.h index 0b0d6d7598..f3ff26c637 100644 --- a/include/sysemu/reset.h +++ b/include/sysemu/reset.h @@ -2,7 +2,11 @@ #define QEMU_SYSEMU_RESET_H typedef void QEMUResetHandler(void *opaque); +typedef struct QEMUResetEntry QEMUResetEntry; +QEMUResetEntry *qemu_get_reset_entry(QEMUResetHandler *func, void *opaque); +void qemu_register_reset_after(QEMUResetEntry *entry, + QEMUResetHandler *func, void *opaque); void qemu_register_reset(QEMUResetHandler *func, void *opaque); void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); void qemu_devices_reset(void); -- Gitee From 03e050611d6dc9909166fd31dd11abf6fd5012ea Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 5 Apr 2020 15:29:16 +0800 Subject: [PATCH 198/486] arm/virt/gic: Construct irqs connection from create_gic Make the irqs can be connected to for individual CPU. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 90 ++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 149e0245d7..0af0a996a1 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -772,6 +772,54 @@ static void create_v2m(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } +static void connect_gic_cpu_irqs(VirtMachineState *vms, int i) +{ + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); + SysBusDevice *gicbusdev = SYS_BUS_DEVICE(vms->gic); + int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + int num_cpus = object_property_get_uint(OBJECT(vms->gic), "num-cpu", NULL); + int gic_type = vms->gic_version; + int irq; + /* Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs we use for the virt board. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(vms->gic, + ppibase + timer_irq[irq])); + } + + if (gic_type == 3) { + qemu_irq irq = qdev_get_gpio_in(vms->gic, + ppibase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + } else if (vms->virt) { + qemu_irq irq = qdev_get_gpio_in(vms->gic, + ppibase + ARCH_GIC_MAINT_IRQ); + sysbus_connect_irq(gicbusdev, i + 4 * num_cpus, irq); + } + + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(vms->gic, ppibase + + VIRTUAL_PMU_IRQ)); + + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); +} + static void create_gic(VirtMachineState *vms, MemoryRegion *mem) { MachineState *ms = MACHINE(vms); @@ -849,47 +897,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. */ for (i = 0; i < smp_cpus; i++) { - DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; - int irq; - /* Mapping from the output timer irq lines from the CPU to the - * GIC PPI inputs we use for the virt board. - */ - const int timer_irq[] = { - [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, - [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, - [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, - [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, - }; - - for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { - qdev_connect_gpio_out(cpudev, irq, - qdev_get_gpio_in(vms->gic, - ppibase + timer_irq[irq])); - } - - if (type == 3) { - qemu_irq irq = qdev_get_gpio_in(vms->gic, - ppibase + ARCH_GIC_MAINT_IRQ); - qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", - 0, irq); - } else if (vms->virt) { - qemu_irq irq = qdev_get_gpio_in(vms->gic, - ppibase + ARCH_GIC_MAINT_IRQ); - sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, irq); - } - - qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, - qdev_get_gpio_in(vms->gic, ppibase - + VIRTUAL_PMU_IRQ)); - - sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); - sysbus_connect_irq(gicbusdev, i + smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); - sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); - sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + connect_gic_cpu_irqs(vms, i); } fdt_add_gic_node(vms); -- Gitee From 06cb0756a01796352861b4d47d59db1bde84ec6f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 12:55:17 +0800 Subject: [PATCH 199/486] intc/gicv3_common: Factor out arm_gicv3_common_cpu_realize The CPU object of hotplugged CPU will be defer-created (during hotplug session), so we must factor out realization code to let it can be applied to individual CPU. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/intc/arm_gicv3_common.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 9884d2e39b..1a11d1986d 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -301,6 +301,16 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, } } +static void arm_gicv3_common_cpu_realize(GICv3State *s, int ncpu) +{ + CPUState *cpu = qemu_get_cpu(ncpu); + + s->cpu[ncpu].cpu = cpu; + s->cpu[ncpu].gic = s; + /* Store GICv3CPUState in CPUARMState gicv3state pointer */ + gicv3_set_gicv3state(cpu, &s->cpu[ncpu]); +} + static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) { GICv3State *s = ARM_GICV3_COMMON(dev); @@ -363,10 +373,7 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) CPUState *cpu = qemu_get_cpu(i); uint64_t cpu_affid; - s->cpu[i].cpu = cpu; - s->cpu[i].gic = s; - /* Store GICv3CPUState in CPUARMState gicv3state pointer */ - gicv3_set_gicv3state(cpu, &s->cpu[i]); + arm_gicv3_common_cpu_realize(s, i); /* Pre-construct the GICR_TYPER: * For our implementation: -- Gitee From 62b5c897e367c3db477a680b7557662347677433 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 10:59:55 +0800 Subject: [PATCH 200/486] intc/gicv3_cpuif: Factor out gicv3_init_one_cpuif The CPU object of hotplugged CPU will be defer-created (during hotplug session), so we must factor out some code to let it can be applied to individual CPU. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/intc/arm_gicv3.c | 5 +- hw/intc/arm_gicv3_cpuif.c | 122 ++++++++++++++++++-------------------- hw/intc/gicv3_internal.h | 2 +- 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 9f5f815db9..40016cb84a 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -382,6 +382,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) GICv3State *s = ARM_GICV3(dev); ARMGICv3Class *agc = ARM_GICV3_GET_CLASS(s); Error *local_err = NULL; + int i; agc->parent_realize(dev, &local_err); if (local_err) { @@ -391,7 +392,9 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); - gicv3_init_cpuif(s); + for (i = 0; i < s->num_cpu; i++) { + gicv3_init_one_cpuif(s, i); + } } static void arm_gicv3_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 85fc369e55..70809bcddd 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -2625,76 +2625,72 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque) gicv3_cpuif_update(cs); } -void gicv3_init_cpuif(GICv3State *s) +void gicv3_init_one_cpuif(GICv3State *s, int ncpu) { /* Called from the GICv3 realize function; register our system * registers with the CPU */ - int i; - - for (i = 0; i < s->num_cpu; i++) { - ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); - GICv3CPUState *cs = &s->cpu[i]; - - /* Note that we can't just use the GICv3CPUState as an opaque pointer - * in define_arm_cp_regs_with_opaque(), because when we're called back - * it might be with code translated by CPU 0 but run by CPU 1, in - * which case we'd get the wrong value. - * So instead we define the regs with no ri->opaque info, and - * get back to the GICv3CPUState from the CPUARMState. + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(ncpu)); + GICv3CPUState *cs = &s->cpu[ncpu]; + + /* Note that we can't just use the GICv3CPUState as an opaque pointer + * in define_arm_cp_regs_with_opaque(), because when we're called back + * it might be with code translated by CPU 0 but run by CPU 1, in + * which case we'd get the wrong value. + * So instead we define the regs with no ri->opaque info, and + * get back to the GICv3CPUState from the CPUARMState. + */ + define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + if (arm_feature(&cpu->env, ARM_FEATURE_EL2) + && cpu->gic_num_lrs) { + int j; + + cs->num_list_regs = cpu->gic_num_lrs; + cs->vpribits = cpu->gic_vpribits; + cs->vprebits = cpu->gic_vprebits; + + /* Check against architectural constraints: getting these + * wrong would be a bug in the CPU code defining these, + * and the implementation relies on them holding. */ - define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); - if (arm_feature(&cpu->env, ARM_FEATURE_EL2) - && cpu->gic_num_lrs) { - int j; - - cs->num_list_regs = cpu->gic_num_lrs; - cs->vpribits = cpu->gic_vpribits; - cs->vprebits = cpu->gic_vprebits; - - /* Check against architectural constraints: getting these - * wrong would be a bug in the CPU code defining these, - * and the implementation relies on them holding. - */ - g_assert(cs->vprebits <= cs->vpribits); - g_assert(cs->vprebits >= 5 && cs->vprebits <= 7); - g_assert(cs->vpribits >= 5 && cs->vpribits <= 8); + g_assert(cs->vprebits <= cs->vpribits); + g_assert(cs->vprebits >= 5 && cs->vprebits <= 7); + g_assert(cs->vpribits >= 5 && cs->vpribits <= 8); - define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo); + define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo); - for (j = 0; j < cs->num_list_regs; j++) { - /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs - * are split into two cp15 regs, LR (the low part, with the - * same encoding as the AArch64 LR) and LRC (the high part). - */ - ARMCPRegInfo lr_regset[] = { - { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .opc1 = 4, .crn = 12, - .crm = 12 + (j >> 3), .opc2 = j & 7, - .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL2_RW, - .readfn = ich_lr_read, - .writefn = ich_lr_write, - }, - { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 4, .crn = 12, - .crm = 14 + (j >> 3), .opc2 = j & 7, - .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL2_RW, - .readfn = ich_lr_read, - .writefn = ich_lr_write, - }, - REGINFO_SENTINEL - }; - define_arm_cp_regs(cpu, lr_regset); - } - if (cs->vprebits >= 6) { - define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo); - } - if (cs->vprebits == 7) { - define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); - } + for (j = 0; j < cs->num_list_regs; j++) { + /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs + * are split into two cp15 regs, LR (the low part, with the + * same encoding as the AArch64 LR) and LRC (the high part). + */ + ARMCPRegInfo lr_regset[] = { + { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, + .crm = 12 + (j >> 3), .opc2 = j & 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_lr_read, + .writefn = ich_lr_write, + }, + { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 12, + .crm = 14 + (j >> 3), .opc2 = j & 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_lr_read, + .writefn = ich_lr_write, + }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, lr_regset); + } + if (cs->vprebits >= 6) { + define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo); + } + if (cs->vprebits == 7) { + define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); } - arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); } + arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); } diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index b9c37453b0..65db012600 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -495,7 +495,7 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs); */ void gicv3_redist_update_lpi_only(GICv3CPUState *cs); void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns); -void gicv3_init_cpuif(GICv3State *s); +void gicv3_init_one_cpuif(GICv3State *s, int ncpu); /** * gicv3_cpuif_update: -- Gitee From dd03bc60712bd41a9606742ea4b769aa8e360655 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 12:49:12 +0800 Subject: [PATCH 201/486] intc/kvm_gicv3: Factor out kvm_arm_gicv3_cpu_realize The CPU object of hotplugged CPU will be defer-created (during hotplug session), so we must factor out realization code to let it can be applied to individual CPU. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/intc/arm_gicv3_kvm.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 5ec5ff9ef6..596b31998b 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -764,6 +764,12 @@ static void vm_change_state_handler(void *opaque, bool running, } } +static void kvm_arm_gicv3_cpu_realize(GICv3State *s, int ncpu) +{ + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(ncpu)); + + define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); +} static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { @@ -790,9 +796,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { - ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); - - define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + kvm_arm_gicv3_cpu_realize(s, i); } /* Try to create the device via the device control API */ -- Gitee From e94b8dc43d416b3ebf316faf14309fe4c4d0b4f0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 6 Apr 2020 11:26:35 +0800 Subject: [PATCH 202/486] hw/intc/gicv3: Add CPU hotplug realize hook GICv3 exposes individual CPU realization capability through this hook. It will be used for hotplugged CPU. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/intc/arm_gicv3.c | 17 ++++++++++++++++- hw/intc/arm_gicv3_common.c | 8 ++++++++ hw/intc/arm_gicv3_kvm.c | 11 +++++++++++ include/hw/intc/arm_gicv3.h | 2 ++ include/hw/intc/arm_gicv3_common.h | 4 ++++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 40016cb84a..9591cfbcc0 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -376,6 +376,19 @@ static const MemoryRegionOps gic_ops[] = { } }; +static void gicv3_cpu_realize(GICv3State *s, int i) +{ + gicv3_init_one_cpuif(s, i); +} + +static void arm_gicv3_cpu_hotplug_realize(GICv3State *s, int ncpu) +{ + ARMGICv3Class *agc = ARM_GICV3_GET_CLASS(s); + + agc->parent_cpu_hotplug_realize(s, ncpu); + gicv3_cpu_realize(s, ncpu); +} + static void arm_gic_realize(DeviceState *dev, Error **errp) { /* Device instance realize function for the GIC sysbus device */ @@ -393,7 +406,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); for (i = 0; i < s->num_cpu; i++) { - gicv3_init_one_cpuif(s, i); + gicv3_cpu_realize(s, i); } } @@ -403,6 +416,8 @@ static void arm_gicv3_class_init(ObjectClass *klass, void *data) ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); ARMGICv3Class *agc = ARM_GICV3_CLASS(klass); + agc->parent_cpu_hotplug_realize = agcc->cpu_hotplug_realize; + agcc->cpu_hotplug_realize = arm_gicv3_cpu_hotplug_realize; agcc->post_load = arm_gicv3_post_load; device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize); } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 1a11d1986d..f8ef6817a4 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -311,6 +311,11 @@ static void arm_gicv3_common_cpu_realize(GICv3State *s, int ncpu) gicv3_set_gicv3state(cpu, &s->cpu[ncpu]); } +static void arm_gicv3_common_cpu_hotplug_realize(GICv3State *s, int ncpu) +{ + arm_gicv3_common_cpu_realize(s, ncpu); +} + static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) { GICv3State *s = ARM_GICV3_COMMON(dev); @@ -371,6 +376,7 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { CPUState *cpu = qemu_get_cpu(i); + uint64_t cpu_affid; arm_gicv3_common_cpu_realize(s, i); @@ -537,12 +543,14 @@ static Property arm_gicv3_common_properties[] = { static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); dc->reset = arm_gicv3_common_reset; dc->realize = arm_gicv3_common_realize; device_class_set_props(dc, arm_gicv3_common_properties); dc->vmsd = &vmstate_gicv3; + agcc->cpu_hotplug_realize = arm_gicv3_common_cpu_hotplug_realize; albifc->arm_linux_init = arm_gic_common_linux_init; } diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 596b31998b..95271e754b 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -76,6 +76,7 @@ struct KVMARMGICv3Class { ARMGICv3CommonClass parent_class; DeviceRealize parent_realize; void (*parent_reset)(DeviceState *dev); + CPUHotplugRealize parent_cpu_hotplug_realize; }; static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level) @@ -771,6 +772,14 @@ static void kvm_arm_gicv3_cpu_realize(GICv3State *s, int ncpu) define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); } +static void kvm_arm_gicv3_cpu_hotplug_realize(GICv3State *s, int ncpu) +{ + KVMARMGICv3Class *kagcc = KVM_ARM_GICV3_GET_CLASS(s); + + kagcc->parent_cpu_hotplug_realize(s, ncpu); + kvm_arm_gicv3_cpu_realize(s, ncpu); +} + static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { GICv3State *s = KVM_ARM_GICV3(dev); @@ -881,6 +890,8 @@ static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass); + kgc->parent_cpu_hotplug_realize = agcc->cpu_hotplug_realize; + agcc->cpu_hotplug_realize = kvm_arm_gicv3_cpu_hotplug_realize; agcc->pre_save = kvm_arm_gicv3_get; agcc->post_load = kvm_arm_gicv3_put; device_class_set_parent_realize(dc, kvm_arm_gicv3_realize, diff --git a/include/hw/intc/arm_gicv3.h b/include/hw/intc/arm_gicv3.h index a81a6ae7ec..e360556bd5 100644 --- a/include/hw/intc/arm_gicv3.h +++ b/include/hw/intc/arm_gicv3.h @@ -26,6 +26,8 @@ struct ARMGICv3Class { ARMGICv3CommonClass parent_class; /*< public >*/ + CPUHotplugRealize parent_cpu_hotplug_realize; + DeviceRealize parent_realize; }; diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index fc38e4b7dc..c208a191ff 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -306,11 +306,15 @@ typedef struct ARMGICv3CommonClass ARMGICv3CommonClass; DECLARE_OBJ_CHECKERS(GICv3State, ARMGICv3CommonClass, ARM_GICV3_COMMON, TYPE_ARM_GICV3_COMMON) +typedef void (*CPUHotplugRealize)(GICv3State *s, int ncpu); + struct ARMGICv3CommonClass { /*< private >*/ SysBusDeviceClass parent_class; /*< public >*/ + CPUHotplugRealize cpu_hotplug_realize; + void (*pre_save)(GICv3State *s); void (*post_load)(GICv3State *s); }; -- Gitee From c950cda47386360e37a89dfa7029d83e33888a40 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 13:04:40 +0800 Subject: [PATCH 203/486] accel/kvm: Add pre-park vCPU support For that KVM do not support dynamic adjustment of vCPU count, we must pre-park all possible vCPU at start. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- accel/kvm/kvm-all.c | 23 +++++++++++++++++++++++ include/sysemu/kvm.h | 1 + 2 files changed, 24 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 8a98446b7c..f2ce5cd45a 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -433,6 +433,29 @@ void kvm_destroy_vcpu(CPUState *cpu) } } +int kvm_create_parked_vcpu(unsigned long vcpu_id) +{ + KVMState *s = kvm_state; + struct KVMParkedVcpu *vcpu = NULL; + int ret; + + DPRINTF("kvm_create_parked_vcpu\n"); + + ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); + if (ret < 0) { + DPRINTF("kvm_create_vcpu failed\n"); + goto err; + } + + vcpu = g_malloc0(sizeof(*vcpu)); + vcpu->vcpu_id = vcpu_id; + vcpu->kvm_fd = ret; + QLIST_INSERT_HEAD(&s->kvm_parked_vcpus, vcpu, node); + +err: + return ret; +} + static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) { struct KVMParkedVcpu *cpu; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 7b22aeb6ae..2623775c27 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -221,6 +221,7 @@ int kvm_has_pit_state2(void); int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); int kvm_has_intx_set_mask(void); +int kvm_create_parked_vcpu(unsigned long vcpu_id); /** * kvm_arm_supports_user_irq -- Gitee From 3ed7dcc4a8ccf443d125e7908d8293b562c68d4b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 13:15:35 +0800 Subject: [PATCH 204/486] intc/gicv3: Add pre-sizing capability to GICv3 Currently GICv3 supports fixed smp_cpus CPUs, and all CPUs are present always. Now we want to pre-sizing GICv3 to support max_cpus CPUs and not all of them are present always, so some sizing codes should be concerned. GIC irqs, GICR and GICC are pre-created for all possible CPUs at start, but only smp_cpus CPUs are realize and irqs of smp_cpus CPUs are connected. Other code changes are mainly for arm_gicv3, and we do little about kvm_arm_gicv3 becasue KVM will deal with the sizing information properly. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 17 +++++++++++---- hw/intc/arm_gicv3.c | 43 +++++++++++++++++++++++++------------- hw/intc/arm_gicv3_common.c | 22 +++++++++++++++++-- hw/intc/arm_gicv3_cpuif.c | 4 ++++ hw/intc/arm_gicv3_kvm.c | 28 ++++++++++++++++++++++++- include/hw/arm/virt.h | 3 ++- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0af0a996a1..b1224fb1e4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -827,14 +827,19 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) SysBusDevice *gicbusdev; const char *gictype; int type = vms->gic_version, i; + /* The max number of CPUs suppored by GIC */ + unsigned int num_cpus = ms->smp.cpus; + /* The number of CPUs present before boot */ unsigned int smp_cpus = ms->smp.cpus; uint32_t nb_redist_regions = 0; + assert(num_cpus >= smp_cpus); + gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); vms->gic = qdev_new(gictype); qdev_prop_set_uint32(vms->gic, "revision", type); - qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); + qdev_prop_set_uint32(vms->gic, "num-cpu", num_cpus); /* Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). */ @@ -846,7 +851,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) if (type == 3) { uint32_t redist0_capacity = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; - uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + uint32_t redist0_count = MIN(num_cpus, redist0_capacity); nb_redist_regions = virt_gicv3_redist_region_count(vms); @@ -867,7 +872,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; qdev_prop_set_uint32(vms->gic, "redist-region-count[1]", - MIN(smp_cpus - redist0_count, redist1_capacity)); + MIN(num_cpus - redist0_count, redist1_capacity)); } } else { if (!kvm_irqchip_in_kernel()) { @@ -894,7 +899,11 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) /* Wire the outputs from each CPU's generic timer and the GICv3 * maintenance interrupt signal to the appropriate GIC PPI inputs, - * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's + * inputs. + * + * The irqs of remaining CPUs (if we has) will be connected during + * hotplugging. */ for (i = 0; i < smp_cpus; i++) { connect_gic_cpu_irqs(vms, i); diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 9591cfbcc0..864d4e4034 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -19,6 +19,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/intc/arm_gicv3.h" +#include "hw/core/cpu.h" #include "gicv3_internal.h" static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) @@ -217,7 +218,9 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) assert(len > 0); for (i = 0; i < s->num_cpu; i++) { - s->cpu[i].seenbetter = false; + if (qemu_get_cpu(i)) { + s->cpu[i].seenbetter = false; + } } /* Find the highest priority pending interrupt in this range. */ @@ -259,16 +262,18 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) * now be the new best one). */ for (i = 0; i < s->num_cpu; i++) { - GICv3CPUState *cs = &s->cpu[i]; + if (qemu_get_cpu(i)) { + GICv3CPUState *cs = &s->cpu[i]; - if (cs->seenbetter) { - cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); - } + if (cs->seenbetter) { + cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); + } - if (!cs->seenbetter && cs->hppi.prio != 0xff && - cs->hppi.irq >= start && cs->hppi.irq < start + len) { - gicv3_full_update_noirqset(s); - break; + if (!cs->seenbetter && cs->hppi.prio != 0xff && + cs->hppi.irq >= start && cs->hppi.irq < start + len) { + gicv3_full_update_noirqset(s); + break; + } } } } @@ -279,7 +284,9 @@ void gicv3_update(GICv3State *s, int start, int len) gicv3_update_noirqset(s, start, len); for (i = 0; i < s->num_cpu; i++) { - gicv3_cpuif_update(&s->cpu[i]); + if (qemu_get_cpu(i)) { + gicv3_cpuif_update(&s->cpu[i]); + } } } @@ -291,7 +298,9 @@ void gicv3_full_update_noirqset(GICv3State *s) int i; for (i = 0; i < s->num_cpu; i++) { - s->cpu[i].hppi.prio = 0xff; + if (qemu_get_cpu(i)) { + s->cpu[i].hppi.prio = 0xff; + } } /* Note that we can guarantee that these functions will not @@ -302,7 +311,9 @@ void gicv3_full_update_noirqset(GICv3State *s) gicv3_update_noirqset(s, GIC_INTERNAL, s->num_irq - GIC_INTERNAL); for (i = 0; i < s->num_cpu; i++) { - gicv3_redist_update_noirqset(&s->cpu[i]); + if (qemu_get_cpu(i)) { + gicv3_redist_update_noirqset(&s->cpu[i]); + } } } @@ -315,7 +326,9 @@ void gicv3_full_update(GICv3State *s) gicv3_full_update_noirqset(s); for (i = 0; i < s->num_cpu; i++) { - gicv3_cpuif_update(&s->cpu[i]); + if (qemu_get_cpu(i)) { + gicv3_cpuif_update(&s->cpu[i]); + } } } @@ -406,7 +419,9 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); for (i = 0; i < s->num_cpu; i++) { - gicv3_cpu_realize(s, i); + if (qemu_get_cpu(i)) { + gicv3_cpu_realize(s, i); + } } } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index f8ef6817a4..a4976b2ba0 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -24,12 +24,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "hw/core/cpu.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "gicv3_internal.h" #include "hw/arm/linux-boot-if.h" +#include "hw/boards.h" #include "sysemu/kvm.h" @@ -377,9 +379,14 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { CPUState *cpu = qemu_get_cpu(i); + MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = NULL; uint64_t cpu_affid; - arm_gicv3_common_cpu_realize(s, i); + if (cpu) { + arm_gicv3_common_cpu_realize(s, i); + } /* Pre-construct the GICR_TYPER: * For our implementation: @@ -393,7 +400,18 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) * VLPIS == 0 (virtual LPIs not supported) * PLPIS == 0 (physical LPIs not supported) */ - cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL); + if (cpu) { + cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL); + } else { + if (!mc->possible_cpu_arch_ids) { + error_report("MachineClass must implement possible_cpu_arch_ids " + "hook to support pre-sizing GICv3"); + exit(1); + } + + possible_cpus = mc->possible_cpu_arch_ids(ms); + cpu_affid = possible_cpus->cpus[i].arch_id; + } /* The CPU mp-affinity property is in MPIDR register format; squash * the affinity bytes into 32 bits as the GICR_TYPER has them. diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 70809bcddd..274a40a40c 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1676,6 +1676,10 @@ static void icc_generate_sgi(CPUARMState *env, GICv3CPUState *cs, aff, targetlist); for (i = 0; i < s->num_cpu; i++) { + if (!qemu_get_cpu(i)) { + continue; + } + GICv3CPUState *ocs = &s->cpu[i]; if (irm) { diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 95271e754b..2e2b08e31f 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -342,6 +342,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { GICv3CPUState *c = &s->cpu[ncpu]; + if (!qemu_get_cpu(ncpu)) { + continue; + } + reg64 = c->gicr_propbaser; regl = (uint32_t)reg64; kvm_gicr_access(s, GICR_PROPBASER, ncpu, ®l, true); @@ -361,6 +365,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { GICv3CPUState *c = &s->cpu[ncpu]; + if (!qemu_get_cpu(ncpu)) { + continue; + } + reg = c->gicr_ctlr; kvm_gicr_access(s, GICR_CTLR, ncpu, ®, true); @@ -457,6 +465,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) GICv3CPUState *c = &s->cpu[ncpu]; int num_pri_bits; + if (!qemu_get_cpu(ncpu)) { + continue; + } + kvm_gicc_access(s, ICC_SRE_EL1, ncpu, &c->icc_sre_el1, true); kvm_gicc_access(s, ICC_CTLR_EL1, ncpu, &c->icc_ctlr_el1[GICV3_NS], true); @@ -524,6 +536,10 @@ static void kvm_arm_gicv3_get(GICv3State *s) /* Redistributor state (one per CPU) */ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + if (!qemu_get_cpu(ncpu)) { + continue; + } + GICv3CPUState *c = &s->cpu[ncpu]; kvm_gicr_access(s, GICR_CTLR, ncpu, ®, false); @@ -559,6 +575,10 @@ static void kvm_arm_gicv3_get(GICv3State *s) if (redist_typer & GICR_TYPER_PLPIS) { for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + if (!qemu_get_cpu(ncpu)) { + continue; + } + GICv3CPUState *c = &s->cpu[ncpu]; kvm_gicr_access(s, GICR_PROPBASER, ncpu, ®l, false); @@ -612,6 +632,10 @@ static void kvm_arm_gicv3_get(GICv3State *s) */ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { + if (!qemu_get_cpu(ncpu)) { + continue; + } + GICv3CPUState *c = &s->cpu[ncpu]; int num_pri_bits; @@ -805,7 +829,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { - kvm_arm_gicv3_cpu_realize(s, i); + if (qemu_get_cpu(i)) { + kvm_arm_gicv3_cpu_realize(s, i); + } } /* Try to create the device via the device control API */ diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 2a838620d8..947d41f767 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -196,8 +196,9 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; assert(vms->gic_version == VIRT_GIC_VERSION_3); + GICv3State *s = ARM_GICV3_COMMON(vms->gic); - return MACHINE(vms)->smp.cpus > redist0_capacity ? 2 : 1; + return s->num_cpu > redist0_capacity ? 2 : 1; } #endif /* QEMU_ARM_VIRT_H */ -- Gitee From 8bd05cdb811e868c54ef28ac558c7efb7cf610b9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 13:40:44 +0800 Subject: [PATCH 205/486] acpi/madt: Add pre-sizing capability to MADT GICC struct The count of possible CPUs is exposed to guest through the count of MADT GICC struct, so we should pre-sizing MADT GICC too. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt-acpi-build.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 7cb320d9f2..a16b54086e 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -787,8 +787,16 @@ void virt_madt_cpu_entry(AcpiDeviceIf *adev, int i, ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); uint64_t physical_base_address = 0, gich = 0, gicv = 0; uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; - uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ? - PPI(VIRTUAL_PMU_IRQ) : 0; + uint32_t pmu_interrupt, enabled; + static bool pmu; + + if (i == 0) { + pmu = arm_feature(&armcpu->env, ARM_FEATURE_PMU); + } + /* FEATURE_PMU should be all enabled or disabled for CPUs */ + assert(!armcpu || arm_feature(&armcpu->env, ARM_FEATURE_PMU) == pmu); + pmu_interrupt = pmu ? PPI(VIRTUAL_PMU_IRQ) : 0; + enabled = armcpu || force_enabled ? 1 /* Enabled */ : 0 /* Disabled */; if (vms->gic_version == 2) { physical_base_address = memmap[VIRT_GIC_CPU].base; @@ -803,7 +811,7 @@ void virt_madt_cpu_entry(AcpiDeviceIf *adev, int i, build_append_int_noprefix(table_data, i, 4); /* GIC ID */ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ /* Flags */ - build_append_int_noprefix(table_data, 1, 4); /* Enabled */ + build_append_int_noprefix(table_data, enabled, 4); /* Enabled */ /* Parking Protocol Version */ build_append_int_noprefix(table_data, 0, 4); /* Performance Interrupt GSIV */ @@ -817,7 +825,7 @@ void virt_madt_cpu_entry(AcpiDeviceIf *adev, int i, build_append_int_noprefix(table_data, vgic_interrupt, 4); build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ /* MPIDR */ - build_append_int_noprefix(table_data, armcpu->mp_affinity, 8); + build_append_int_noprefix(table_data, possible_cpus->cpus[i].arch_id, 8); } static void @@ -825,9 +833,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i; VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); + MachineClass *mc = MACHINE_GET_CLASS(vms); + MachineState *ms = MACHINE(vms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); const MemMapEntry *memmap = vms->memmap; AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + /* The MADT GICC numbers */ + int num_cpu = ms->smp.cpus; acpi_table_begin(&table, table_data); /* Local Interrupt Controller Address */ @@ -846,8 +859,8 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, vms->gic_version, 1); build_append_int_noprefix(table_data, 0, 3); /* Reserved */ - for (i = 0; i < MACHINE(vms)->smp.cpus; i++) { - virt_madt_cpu_entry(NULL, i, NULL, table_data, false); + for (i = 0; i < num_cpu; i++) { + virt_madt_cpu_entry(NULL, i, possible_cpus, table_data, false); } if (vms->gic_version == 3) { -- Gitee From 965eb25b03f6977a7656dce3ac5cdb4c95bfe003 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 13:50:40 +0800 Subject: [PATCH 206/486] arm/virt: Add cpu_hotplug_enabled field Some conditions must be satisfied to support CPU hotplug, including ACPI, GED, 64bit CPU, GICv3. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 7 +++++++ include/hw/arm/virt.h | 1 + 2 files changed, 8 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index b1224fb1e4..45a0a045b1 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2008,6 +2008,7 @@ static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine); + MachineState *ms = MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *possible_cpus; MemoryRegion *sysmem = get_system_memory(); @@ -2017,6 +2018,7 @@ static void machvirt_init(MachineState *machine) bool has_ged = !vmc->no_ged; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; + ObjectClass *cpu_class; /* * In accelerated mode, the memory map is computed earlier in kvm_type() @@ -2106,6 +2108,11 @@ static void machvirt_init(MachineState *machine) create_fdt(vms); qemu_log("cpu init start\n"); + cpu_class = object_class_by_name(ms->cpu_type); + vms->cpu_hotplug_enabled = has_ged && firmware_loaded && + virt_is_acpi_enabled(vms) && vms->gic_version == 3 && + !!object_class_dynamic_cast(cpu_class, TYPE_AARCH64_CPU); + possible_cpus = mc->possible_cpu_arch_ids(machine); assert(possible_cpus->len == max_cpus); for (n = 0; n < possible_cpus->len; n++) { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 947d41f767..c371d377e0 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -149,6 +149,7 @@ struct VirtMachineState { bool its; bool tcg_its; bool virt; + bool cpu_hotplug_enabled; bool ras; bool mte; OnOffAuto acpi; -- Gitee From e3522e63a2f14c3c7d8cd603099b6bb51087f43b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 22 Apr 2020 19:52:58 +0800 Subject: [PATCH 207/486] arm/virt/acpi: Extend cpufreq to support max_cpus We will support CPU hotplug soon, so extend memory region size to allow hotplugged CPU access cpufreq space. Signed-off-by: Keqian Zhu --- hw/acpi/cpufreq.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/hw/acpi/cpufreq.c b/hw/acpi/cpufreq.c index a84db490b3..a76f7b8fa2 100644 --- a/hw/acpi/cpufreq.c +++ b/hw/acpi/cpufreq.c @@ -83,6 +83,7 @@ typedef struct CpuhzState { uint32_t PerformanceLimited; uint32_t LowestFreq; uint32_t NominalFreq; + uint32_t num_cpu; uint32_t reg_size; } CpuhzState; @@ -93,10 +94,7 @@ static uint64_t cpufreq_read(void *opaque, hwaddr offset, unsigned size) uint64_t r; uint64_t n; - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; - - if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) { + if (offset >= s->num_cpu * CPPC_REG_PER_CPU_STRIDE) { warn_report("cpufreq_read: offset 0x%lx out of range", offset); return 0; } @@ -163,11 +161,10 @@ static uint64_t cpufreq_read(void *opaque, hwaddr offset, unsigned size) static void cpufreq_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + CpuhzState *s = CPUFREQ(opaque); uint64_t n; - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; - if (offset >= smp_cpus * CPPC_REG_PER_CPU_STRIDE) { + if (offset >= s->num_cpu * CPPC_REG_PER_CPU_STRIDE) { error_printf("cpufreq_write: offset 0x%lx out of range", offset); return; } @@ -248,9 +245,9 @@ static void cpufreq_init(Object *obj) CpuhzState *s = CPUFREQ(obj); MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int smp_cpus = ms->smp.cpus; + s->num_cpu = ms->smp.max_cpus; - s->reg_size = smp_cpus * CPPC_REG_PER_CPU_STRIDE; + s->reg_size = s->num_cpu * CPPC_REG_PER_CPU_STRIDE; if (s->reg_size > MAX_SUPPORT_SPACE) { error_report("Required space 0x%x excesses the max support 0x%x", s->reg_size, MAX_SUPPORT_SPACE); -- Gitee From 3063d421cd68937ece290bc02151cc15b7ec33d0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 13:55:11 +0800 Subject: [PATCH 208/486] arm/virt: Pre-sizing MADT-GICC GICv3 and Pre-park KVM vCPU Establish all pre-sizing facilities based on cpu_hotplug_enabled field. Note: PPTT is constructed for possible_cpus, so it does not need to pre-sizing it. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt-acpi-build.c | 3 +++ hw/arm/virt.c | 14 ++++++++++++-- target/arm/kvm.c | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index a16b54086e..1101161d70 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -859,6 +859,9 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, vms->gic_version, 1); build_append_int_noprefix(table_data, 0, 3); /* Reserved */ + if (vms->cpu_hotplug_enabled) { + num_cpu = ms->smp.max_cpus; + } for (i = 0; i < num_cpu; i++) { virt_madt_cpu_entry(NULL, i, possible_cpus, table_data, false); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 45a0a045b1..4eb1b44729 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -833,6 +833,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) unsigned int smp_cpus = ms->smp.cpus; uint32_t nb_redist_regions = 0; + if (vms->cpu_hotplug_enabled) { + num_cpus = ms->smp.max_cpus; + } assert(num_cpus >= smp_cpus); gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); @@ -2119,8 +2122,15 @@ static void machvirt_init(MachineState *machine) Object *cpuobj; CPUState *cs; + if (kvm_enabled() && vms->cpu_hotplug_enabled) { + if (kvm_create_parked_vcpu(n) < 0) { + error_report("mach-virt: Create KVM parked vCPU failed"); + exit(1); + } + } + if (n >= smp_cpus) { - break; + continue; } cpuobj = object_new(possible_cpus->cpus[n].type); @@ -2208,7 +2218,7 @@ static void machvirt_init(MachineState *machine) } vms->bootinfo.ram_size = machine->ram_size; - vms->bootinfo.nb_cpus = smp_cpus; + vms->bootinfo.nb_cpus = vms->cpu_hotplug_enabled ? max_cpus : smp_cpus; vms->bootinfo.board_id = -1; vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base; vms->bootinfo.get_dtb = machvirt_dtb; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 59d556724f..29ac3f40e0 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -262,9 +262,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); - if (ms->smp.cpus > 256 && + if (ms->smp.max_cpus > 256 && !kvm_check_extension(s, KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)) { - error_report("Using more than 256 vcpus requires a host kernel " + error_report("Using more than max 256 vcpus requires a host kernel " "with KVM_CAP_ARM_IRQ_LINE_LAYOUT_2"); ret = -EINVAL; } -- Gitee From a2d8cf86a379bb161cdae850824c9e80fb370599 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 Apr 2020 14:16:40 +0800 Subject: [PATCH 209/486] arm/virt: Start up CPU hot-plug and cold-plug All the CPU hotplug facilities are ready. Assemble them to start up CPU hot-plug capability for arm/virt. This also adds CPU cold plug support to arm virt machine board. CPU cold plug means adding CPU by using "-device xx-arm-cpu" when we bring up Qemu. Signed-off-by: Salil Mehta Signed-off-by: Keqian Zhu --- hw/arm/virt.c | 110 ++++++++++++++++++++++++++++++++++++++++-- hw/core/cpu-common.c | 4 ++ include/hw/arm/virt.h | 1 + target/arm/cpu.c | 2 + 4 files changed, 113 insertions(+), 4 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4eb1b44729..b81d22d68f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -52,6 +52,8 @@ #include "sysemu/tpm.h" #include "sysemu/kvm.h" #include "sysemu/hvf.h" +#include "sysemu/cpus.h" +#include "sysemu/hw_accel.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" @@ -703,9 +705,9 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) event |= ACPI_GED_NVDIMM_HOTPLUG_EVT; } - /* event |= ACPI_GED_CPU_HOTPLUG_EVT; - * Currently CPU hotplug is not enabled. - */ + if (vms->cpu_hotplug_enabled) { + event |= ACPI_GED_CPU_HOTPLUG_EVT; + } dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); @@ -2555,11 +2557,18 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(hotplug_dev); const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + const CPUArchId *cpu_slot = NULL; MemoryRegion *sysmem = get_system_memory(); int smp_clusters = ms->smp.clusters; int smp_cores = ms->smp.cores; int smp_threads = ms->smp.threads; + if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { + error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", + ms->cpu_type); + return; + } + /* if cpu idx is not set, set it based on socket/cluster/core/thread * properties */ @@ -2593,6 +2602,20 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, cs->cpu_index = idx_from_topo_ids(smp_clusters, smp_cores, smp_threads, &topo); } + /* Some hotplug capability checks */ + if (cs->cpu_index >= ms->smp.cpus) { + if (!vms->acpi_dev) { + error_setg(errp, "CPU cold/hot plug is disabled: " + "missing acpi device."); + return; + } + if (!vms->cpu_hotplug_enabled) { + error_setg(errp, "CPU cold/hot plug is disabled: " + "should use AArch64 CPU and GICv3."); + return; + } + } + /* if 'address' properties socket-id/cluster-id/core-id/thread-id are not * set, set them so that machine_query_hotpluggable_cpus would show correct * values @@ -2631,6 +2654,13 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, object_property_set_int(cpuobj, "mp-affinity", possible_cpus->cpus[cs->cpu_index].arch_id, NULL); + cpu_slot = &possible_cpus->cpus[cs->cpu_index]; + if (cpu_slot->cpu) { + error_setg(errp, "CPU[%d] with mp_affinity %" PRIu64 " exists", + cs->cpu_index, cpu->mp_affinity); + return; + } + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), &error_fatal); @@ -2716,12 +2746,83 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, &error_abort); } } + + /* If we use KVM accel, we should pause all vcpus to + * allow hot access of vcpu registers. + */ + if (dev->hotplugged && kvm_enabled()) { + pause_all_vcpus(); + } } static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - /* Currently nothing to do */ + CPUArchId *cpu_slot; + CPUState *cs = CPU(dev); + int ncpu = cs->cpu_index; + MachineState *ms = MACHINE(hotplug_dev); + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + bool pmu = object_property_get_bool(OBJECT(first_cpu), "pmu", NULL); + bool steal_time = object_property_get_bool(OBJECT(first_cpu), + "kvm-steal-time", NULL); + GICv3State *gicv3; + ARMGICv3CommonClass *agcc; + Error *local_err = NULL; + + /* For CPU that is cold/hot plugged */ + if (ncpu >= ms->smp.cpus) { + /* Realize GIC related parts of CPU */ + assert(vms->gic_version == 3); + gicv3 = ARM_GICV3_COMMON(vms->gic); + agcc = ARM_GICV3_COMMON_GET_CLASS(gicv3); + agcc->cpu_hotplug_realize(gicv3, ncpu); + connect_gic_cpu_irqs(vms, ncpu); + + /* Init PMU and steal_time part */ + if (kvm_enabled()) { + hwaddr pvtime_reg_base = vms->memmap[VIRT_PVTIME].base; + + if (pmu) { + assert(arm_feature(&ARM_CPU(cs)->env, ARM_FEATURE_PMU)); + if (kvm_irqchip_in_kernel()) { + kvm_arm_pmu_set_irq(cs, PPI(VIRTUAL_PMU_IRQ)); + } + kvm_arm_pmu_init(cs); + } + if (steal_time) { + kvm_arm_pvtime_init(cs, pvtime_reg_base + + ncpu * PVTIME_SIZE_PER_CPU); + } + } + + /* Register CPU reset and trigger it manually */ + cpu_synchronize_state(cs); + cpu_hotplug_register_reset(ncpu); + cpu_hotplug_reset_manually(ncpu); + cpu_synchronize_post_reset(cs); + } + + if (dev->hotplugged && kvm_enabled()) { + resume_all_vcpus(); + } + + if (vms->acpi_dev) { + hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err); + if (local_err) { + goto out; + } + } + + vms->boot_cpus++; + if (vms->fw_cfg) { + fw_cfg_modify_i16(vms->fw_cfg, FW_CFG_NB_CPUS, vms->boot_cpus); + } + + cpu_slot = &ms->possible_cpus->cpus[ncpu]; + cpu_slot->cpu = OBJECT(dev); +out: + error_propagate(errp, local_err); } static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, @@ -2940,6 +3041,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; mc->kvm_type = virt_kvm_type; + mc->has_hotpluggable_cpus = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; hc->pre_plug = virt_machine_device_pre_plug_cb; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 9e3241b430..b8d1d820cb 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -208,6 +208,10 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) if (dev->hotplugged) { cpu_synchronize_post_init(cpu); + +#ifdef __aarch64__ + if (!kvm_enabled()) +#endif cpu_resume(cpu); } diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index c371d377e0..4ddee19b18 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -168,6 +168,7 @@ struct VirtMachineState { uint32_t msi_phandle; uint32_t iommu_phandle; int psci_conduit; + uint32_t boot_cpus; hwaddr highest_gpa; DeviceState *gic; DeviceState *acpi_dev; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 9fd8e57971..d550022f18 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2580,6 +2580,8 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, arm_cpu_properties); device_class_set_parent_reset(dc, arm_cpu_reset, &acc->parent_reset); + dc->user_creatable = true; + cc->class_by_name = arm_cpu_class_by_name; cc->has_work = arm_cpu_has_work; cc->dump_state = arm_cpu_dump_state; -- Gitee From 92e9fb334c38cd21652ce8adde9ec01ab4412426 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Tue, 15 Feb 2022 15:18:17 +0800 Subject: [PATCH 210/486] Revert "qmp: add command to query used memslots of vhost-net and vhost-user" This reverts commit 1545a60a8b78490c7dc8909b7012bca63dba63cd. Signed-off-by: Jinhua Cao --- hw/virtio/vhost-backend.c | 2 +- hw/virtio/vhost-user.c | 2 +- include/hw/virtio/vhost-backend.h | 2 -- monitor/qmp-cmds.c | 12 ------------ qapi/net.json | 18 ------------------ qapi/pragma.json | 4 +--- 6 files changed, 3 insertions(+), 37 deletions(-) diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index d8e1710758..2acfb750fd 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -300,7 +300,7 @@ static void vhost_kernel_set_used_memslots(struct vhost_dev *dev) vhost_kernel_used_memslots = dev->mem->nregions; } -unsigned int vhost_kernel_get_used_memslots(void) +static unsigned int vhost_kernel_get_used_memslots(void) { return vhost_kernel_used_memslots; } diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 8f69a3b850..176cae9244 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2544,7 +2544,7 @@ static void vhost_user_set_used_memslots(struct vhost_dev *dev) vhost_user_used_memslots = counter; } -unsigned int vhost_user_get_used_memslots(void) +static unsigned int vhost_user_get_used_memslots(void) { return vhost_user_used_memslots; } diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 7bbc658161..a64708f456 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -190,6 +190,4 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); -unsigned int vhost_kernel_get_used_memslots(void); -unsigned int vhost_user_get_used_memslots(void); #endif /* VHOST_BACKEND_H */ diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index a138e7dd4b..d71beace6a 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -37,7 +37,6 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-ui.h" -#include "qapi/qapi-commands-net.h" #include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" #include "exec/ramlist.h" @@ -45,7 +44,6 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" -#include "hw/virtio/vhost-backend.h" NameInfo *qmp_query_name(Error **errp) { @@ -476,13 +474,3 @@ int64_t qmp_query_rtc_date_diff(Error **errp) { return get_rtc_date_diff(); } - -uint32_t qmp_query_vhost_kernel_used_memslots(Error **errp) -{ - return vhost_kernel_get_used_memslots(); -} - -uint32_t qmp_query_vhost_user_used_memslots(Error **errp) -{ - return vhost_user_get_used_memslots(); -} diff --git a/qapi/net.json b/qapi/net.json index c9ff849eed..7fab2e7cd8 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -696,21 +696,3 @@ ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } - -## -# @query-vhost-kernel-used-memslots: -# -# Get vhost-kernel nic used memslots -# -# Since: 4.1 -## -{ 'command': 'query-vhost-kernel-used-memslots', 'returns': 'uint32' } - -## -# @query-vhost-user-used-memslots: -# -# Get vhost-user nic used memslots -# -# Since: 4.1 -## -{ 'command': 'query-vhost-user-used-memslots', 'returns': 'uint32' } diff --git a/qapi/pragma.json b/qapi/pragma.json index d35c897acb..b37f6de445 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -27,9 +27,7 @@ 'query-tpm-models', 'query-tpm-types', 'ringbuf-read', - 'query-rtc-date-diff', - 'query-vhost-user-used-memslots', - 'query-vhost-kernel-used-memslots' ], + 'query-rtc-date-diff' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status -- Gitee From 6a5a391c9c6f6c0cd105ce3495acc10868bad884 Mon Sep 17 00:00:00 2001 From: Dongxu Sun Date: Tue, 15 Feb 2022 14:40:48 +0800 Subject: [PATCH 211/486] target/arm: Fix some compile errors fix compile errors like: "implicit declaration of function 'kvm_arm_cpu_feature_supported'"; "undefined reference to 'kvm_arm_get_one_reg'" "undefined reference to 'kvm_arm_set_one_reg'" "'kvmval' may be used uninitialized" "'oldval' may be used uninitialized" Signed-off-by: Dongxu Sun --- target/arm/helper.c | 4 ++-- target/arm/kvm_arm.h | 23 ++++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1dd5d64d96..80737a8d7b 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -168,8 +168,8 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) if (kvm_sync) { if (is_id_reg(ri)) { /* Only sync if we can sync to KVM successfully. */ - uint64_t oldval; - uint64_t kvmval; + uint64_t oldval = 0; + uint64_t kvmval = 0; if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &oldval)) { continue; diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 82145607ec..8b644b3924 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -377,6 +377,9 @@ void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa); int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); +int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *target); +int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *source); + #else /* @@ -403,6 +406,11 @@ static inline bool kvm_arm_steal_time_supported(void) return false; } +static inline bool kvm_arm_cpu_feature_supported(void) +{ + return false; +} + /* * These functions should never actually be called without KVM support. */ @@ -451,6 +459,18 @@ static inline void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map) g_assert_not_reached(); } +static inline int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, + uint64_t *target) +{ + g_assert_not_reached(); +} + +static inline int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, + uint64_t *source) +{ + g_assert_not_reached(); +} + #endif static inline const char *gic_class_name(void) @@ -535,7 +555,4 @@ static inline const char *its_class_name(void) } } -int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *target); -int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *source); - #endif -- Gitee From f8e5f099c5b6665e3ed9f397ddca9283148938a4 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 15 Feb 2022 17:02:08 +0800 Subject: [PATCH 212/486] pl031: support rtc-timer property for pl031 This patch adds the rtc-timer property for pl031, we can get the rtc time (UTC) through qmp command "qom-get date" with this property. Signed-off-by: Haibin Wang Reviewed-by: Shannon Zhao Reviewed-by: Ying Fang Signed-off-by: Keqian Zhu Signed-off-by: Jinhao Gao --- hw/rtc/pl031.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index da8b061e91..61a2948f77 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -63,6 +63,15 @@ static uint32_t pl031_get_count(PL031State *s) return s->tick_offset + now / NANOSECONDS_PER_SECOND; } +static void pl031_get_date(Object *obj, struct tm *current_tm, Error **errp) +{ + PL031State *s = PL031(obj); + time_t ti = pl031_get_count(s); + + /* Changed to UTC time */ + gmtime_r(&ti, current_tm); +} + static void pl031_set_alarm(PL031State *s) { uint32_t ticks; @@ -201,6 +210,20 @@ static void pl031_init(Object *obj) qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s); + object_property_add_tm(OBJECT(s), "date", pl031_get_date); +} + +static void pl031_realize(DeviceState *d, Error **errp) +{ + object_property_add_alias(qdev_get_machine(), "rtc-time", + OBJECT(d), "date"); +} + +static void pl031_unrealize(DeviceState *d) +{ + if (object_property_find(qdev_get_machine(), "rtc-time")) { + object_property_del(qdev_get_machine(), "rtc-time"); + } } static void pl031_finalize(Object *obj) @@ -337,6 +360,8 @@ static void pl031_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_pl031; + dc->realize = pl031_realize; + dc->unrealize = pl031_unrealize; device_class_set_props(dc, pl031_properties); } -- Gitee From 11498c2d92e703923d373b64ad3f33aec5f383f2 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 17 Feb 2022 09:51:13 +0800 Subject: [PATCH 213/486] i386/cpu: fix compile error in all target configure When compile with `./configure && make -j`, there will be error: "unknown type name `ram_addr_t`", fix the error by adding compilation macro to control it. Signed-off-by: Jiajie Li --- target/i386/cpu.c | 16 ++++++++-------- target/i386/cpu.h | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a4732a7372..d9dca1dafb 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6711,14 +6711,6 @@ static bool x86_cpu_get_paging_enabled(const CPUState *cs) return cpu->env.cr[0] & CR0_PG_MASK; } -#endif /* !CONFIG_USER_ONLY */ - -static void x86_cpu_set_pc(CPUState *cs, vaddr value) -{ - X86CPU *cpu = X86_CPU(cs); - - cpu->env.eip = value; -} /* At present, we check the vm is *LARGE* or not, i.e. whether * the memory size is more than 4T or not. @@ -6736,6 +6728,14 @@ void x86_cpu_adjuest_by_ram_size(ram_addr_t ram_size, X86CPU *cpu) cpu->fill_mtrr_mask = true; } } +#endif /* !CONFIG_USER_ONLY */ + +static void x86_cpu_set_pc(CPUState *cs, vaddr value) +{ + X86CPU *cpu = X86_CPU(cs); + + cpu->env.eip = value; +} int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 6f777fd6ca..d9296a9abc 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1842,10 +1842,12 @@ struct X86CPU { extern const VMStateDescription vmstate_x86_cpu; #endif +#ifndef CONFIG_USER_ONLY #define DEFAULT_VM_CPU_PHYS_BITS 42 #define LARGE_VM_CPU_PHYS_BITS 46 void x86_cpu_adjuest_by_ram_size(ram_addr_t ram_size, X86CPU *cpu); +#endif int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); -- Gitee From 239ffdcf42e0795b5f025f87fa19ce01642811f2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 1 Mar 2022 20:12:12 +0800 Subject: [PATCH 214/486] scsi-bus: fix unmatched object_unref() Fix commit 391dd8f1("scsi-bus: Refactor the code that retries requests"), which split scsi_dma_restart_bh(), but the object_unref() belongs to scsi_dma_restart_bh(). So, we should mv object_unref() from scsi_retry_requests() to scsi_dma_restart_bh(). Signed-off-by: Yan Wang --- hw/scsi/scsi-bus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 5e6f891b9d..9d37f490ce 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -166,8 +166,6 @@ void scsi_retry_requests(SCSIDevice *s) scsi_req_unref(req); } aio_context_release(blk_get_aio_context(s->conf.blk)); - /* Drop the reference that was acquired in scsi_dma_restart_cb */ - object_unref(OBJECT(s)); } static void scsi_dma_restart_bh(void *opaque) @@ -178,6 +176,9 @@ static void scsi_dma_restart_bh(void *opaque) s->bh = NULL; scsi_retry_requests(s); + + /* Drop the reference that was acquired in scsi_dma_restart_cb */ + object_unref(OBJECT(s)); } void scsi_req_retry(SCSIRequest *req) -- Gitee From 5ca1beec7030b5d9fea36eb4f037d4e0e6c260bd Mon Sep 17 00:00:00 2001 From: Christian Ehrhardt Date: Wed, 9 Feb 2022 12:14:56 +0100 Subject: [PATCH 215/486] tools/virtiofsd: Add rseq syscall to the seccomp allowlist The virtiofsd currently crashes when used with glibc 2.35. That is due to the rseq system call being added to every thread creation [1][2]. [1]: https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/ [2]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html This happens not at daemon start, but when a guest connects /usr/lib/qemu/virtiofsd -f --socket-path=/tmp/testvfsd -o sandbox=chroot \ -o source=/var/guests/j-virtiofs --socket-group=kvm virtio_session_mount: Waiting for vhost-user socket connection... # start ok, now guest will connect virtio_session_mount: Received vhost-user socket connection virtio_loop: Entry fv_queue_set_started: qidx=0 started=1 fv_queue_set_started: qidx=1 started=1 Bad system call (core dumped) We have to put rseq on the seccomp allowlist to avoid that the daemon is crashing in this case. Reported-by: Michael Hudson-Doyle Signed-off-by: Christian Ehrhardt Reviewed-by: Dr. David Alan Gilbert Message-id: 20220209111456.3328420-1-christian.ehrhardt@canonical.com [Moved rseq to its alphabetically ordered position in the seccomp allowlist. --Stefan] Signed-off-by: Stefan Hajnoczi Signed-off-by: qinyu --- tools/virtiofsd/passthrough_seccomp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/virtiofsd/passthrough_seccomp.c b/tools/virtiofsd/passthrough_seccomp.c index a3ce9f898d..2bc0127b69 100644 --- a/tools/virtiofsd/passthrough_seccomp.c +++ b/tools/virtiofsd/passthrough_seccomp.c @@ -91,6 +91,9 @@ static const int syscall_allowlist[] = { SCMP_SYS(renameat2), SCMP_SYS(removexattr), SCMP_SYS(restart_syscall), +#ifdef __NR_rseq + SCMP_SYS(rseq), /* required since glibc 2.35 */ +#endif SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigreturn), -- Gitee From d74713d8e656e8d0f7a5200122119802e639ff49 Mon Sep 17 00:00:00 2001 From: Lu Feifei Date: Mon, 14 Mar 2022 11:04:02 +0800 Subject: [PATCH 216/486] sw_64: Add sw64 architecture support Signed-off-by: Lu Feifei --- configs/devices/sw64-softmmu/default.mak | 10 + configs/targets/sw64-softmmu.mak | 8 + configure | 7 + disas.c | 2 + disas/meson.build | 1 + disas/sw64.c | 1242 +++++++ hw/Kconfig | 1 + hw/meson.build | 1 + hw/rtc/sun4v-rtc.c | 11 + hw/sw64/Kconfig | 11 + hw/sw64/Makefile.objs | 1 + hw/sw64/core.h | 25 + hw/sw64/core3.c | 182 ++ hw/sw64/core3_board.c | 493 +++ hw/sw64/meson.build | 10 + hw/sw64/sw64_iommu.c | 567 ++++ hw/sw64/trace-events | 3 + include/disas/dis-asm.h | 4 + include/elf.h | 44 + include/hw/sw64/sw64_iommu.h | 105 + include/qemu/atomic.h | 2 + include/qemu/timer.h | 10 + include/sysemu/arch_init.h | 1 + linux-headers/asm-sw64/kvm.h | 122 + linux-headers/asm-sw64/unistd.h | 380 +++ linux-user/meson.build | 1 + linux-user/sw64/cpu_loop.c | 108 + linux-user/sw64/signal.c | 273 ++ linux-user/sw64/sockbits.h | 1 + linux-user/sw64/syscall_nr.h | 471 +++ linux-user/sw64/target_cpu.h | 38 + linux-user/sw64/target_elf.h | 14 + linux-user/sw64/target_fcntl.h | 11 + linux-user/sw64/target_signal.h | 98 + linux-user/sw64/target_structs.h | 47 + linux-user/sw64/target_syscall.h | 121 + linux-user/sw64/termbits.h | 265 ++ meson.build | 12 +- pc-bios/core3-hmcode | Bin 0 -> 225904 bytes pc-bios/core3-reset | Bin 0 -> 5032 bytes pc-bios/core4-hmcode | Bin 0 -> 243040 bytes pc-bios/meson.build | 3 + pc-bios/uefi-bios-sw | Bin 0 -> 3145728 bytes qapi/machine.json | 2 +- softmmu/qdev-monitor.c | 3 +- target/Kconfig | 1 + target/meson.build | 1 + target/sw64/Kconfig | 2 + target/sw64/Makefile.objs | 4 + target/sw64/cpu-param.h | 24 + target/sw64/cpu-qom.h | 47 + target/sw64/cpu.c | 457 +++ target/sw64/cpu.h | 406 +++ target/sw64/exception.c | 76 + target/sw64/float_helper.c | 846 +++++ target/sw64/helper.c | 349 ++ target/sw64/helper.h | 127 + target/sw64/int_helper.c | 118 + target/sw64/kvm.c | 215 ++ target/sw64/kvm_sw64.h | 47 + target/sw64/machine.c | 18 + target/sw64/meson.build | 19 + target/sw64/profile.c | 2342 +++++++++++++ target/sw64/profile.h | 541 +++ target/sw64/simd_helper.c | 1058 ++++++ target/sw64/translate.c | 3798 ++++++++++++++++++++++ target/sw64/translate.h | 60 + tcg/sw64/tcg-target-con-set.h | 39 + tcg/sw64/tcg-target-con-str.h | 28 + tcg/sw64/tcg-target.c.inc | 2109 ++++++++++++ tcg/sw64/tcg-target.h | 123 + tcg/sw64/tcg-target.opc.h | 15 + 72 files changed, 17578 insertions(+), 3 deletions(-) create mode 100644 configs/devices/sw64-softmmu/default.mak create mode 100644 configs/targets/sw64-softmmu.mak create mode 100755 disas/sw64.c create mode 100644 hw/sw64/Kconfig create mode 100644 hw/sw64/Makefile.objs create mode 100644 hw/sw64/core.h create mode 100644 hw/sw64/core3.c create mode 100644 hw/sw64/core3_board.c create mode 100644 hw/sw64/meson.build create mode 100644 hw/sw64/sw64_iommu.c create mode 100644 hw/sw64/trace-events create mode 100644 include/hw/sw64/sw64_iommu.h create mode 100644 linux-headers/asm-sw64/kvm.h create mode 100644 linux-headers/asm-sw64/unistd.h create mode 100644 linux-user/sw64/cpu_loop.c create mode 100644 linux-user/sw64/signal.c create mode 100644 linux-user/sw64/sockbits.h create mode 100644 linux-user/sw64/syscall_nr.h create mode 100644 linux-user/sw64/target_cpu.h create mode 100644 linux-user/sw64/target_elf.h create mode 100644 linux-user/sw64/target_fcntl.h create mode 100644 linux-user/sw64/target_signal.h create mode 100644 linux-user/sw64/target_structs.h create mode 100644 linux-user/sw64/target_syscall.h create mode 100644 linux-user/sw64/termbits.h create mode 100755 pc-bios/core3-hmcode create mode 100755 pc-bios/core3-reset create mode 100755 pc-bios/core4-hmcode create mode 100755 pc-bios/uefi-bios-sw create mode 100644 target/sw64/Kconfig create mode 100644 target/sw64/Makefile.objs create mode 100644 target/sw64/cpu-param.h create mode 100644 target/sw64/cpu-qom.h create mode 100644 target/sw64/cpu.c create mode 100644 target/sw64/cpu.h create mode 100644 target/sw64/exception.c create mode 100644 target/sw64/float_helper.c create mode 100644 target/sw64/helper.c create mode 100644 target/sw64/helper.h create mode 100644 target/sw64/int_helper.c create mode 100644 target/sw64/kvm.c create mode 100644 target/sw64/kvm_sw64.h create mode 100644 target/sw64/machine.c create mode 100644 target/sw64/meson.build create mode 100644 target/sw64/profile.c create mode 100644 target/sw64/profile.h create mode 100644 target/sw64/simd_helper.c create mode 100644 target/sw64/translate.c create mode 100644 target/sw64/translate.h create mode 100755 tcg/sw64/tcg-target-con-set.h create mode 100755 tcg/sw64/tcg-target-con-str.h create mode 100755 tcg/sw64/tcg-target.c.inc create mode 100755 tcg/sw64/tcg-target.h create mode 100755 tcg/sw64/tcg-target.opc.h diff --git a/configs/devices/sw64-softmmu/default.mak b/configs/devices/sw64-softmmu/default.mak new file mode 100644 index 0000000000..0b4d56b43e --- /dev/null +++ b/configs/devices/sw64-softmmu/default.mak @@ -0,0 +1,10 @@ +# Default configuration for sw64-softmmu + +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n +#CONFIG_TEST_DEVICES=n + +# Boards: +# +CONFIG_CORE3=y diff --git a/configs/targets/sw64-softmmu.mak b/configs/targets/sw64-softmmu.mak new file mode 100644 index 0000000000..37cc2e05a6 --- /dev/null +++ b/configs/targets/sw64-softmmu.mak @@ -0,0 +1,8 @@ +# Default configuration for sw64-softmmu + +# Boards: +# +TARGET_ARCH=sw64 +TARGET_BASE_ARCH=sw64 +TARGET_ABI_DIR=sw64 +TARGET_SUPPORTS_MTTCG=y diff --git a/configure b/configure index 48c21775f3..9569d7a3d0 100755 --- a/configure +++ b/configure @@ -612,6 +612,9 @@ case "$cpu" in sparc|sun4[cdmuv]) cpu="sparc" ;; + sw_64) + cpu="sw64" + ;; *) # This will result in either an error or falling back to TCI later ARCH=unknown @@ -3268,6 +3271,10 @@ alpha) # Ensure there's only a single GP QEMU_CFLAGS="-msmall-data $QEMU_CFLAGS" ;; +sw*) + # Ensure there's only a single GP + QEMU_CFLAGS="-msmall-data $QEMU_CFLAGS" +;; esac if test "$gprof" = "yes" ; then diff --git a/disas.c b/disas.c index 3dab4482d1..897de1d9a9 100644 --- a/disas.c +++ b/disas.c @@ -207,6 +207,8 @@ static void initialize_debug_host(CPUDebug *s) s->info.cap_insn_split = 6; #elif defined(__hppa__) s->info.print_insn = print_insn_hppa; +#elif defined(__sw_64__) + s->info.print_insn = print_insn_sw_64; #endif } diff --git a/disas/meson.build b/disas/meson.build index 449f99e1de..5c5daa69a7 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -20,4 +20,5 @@ common_ss.add(when: 'CONFIG_S390_DIS', if_true: files('s390.c')) common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) +common_ss.add(when: 'CONFIG_SW64_DIS', if_true: files('sw64.c')) common_ss.add(when: capstone, if_true: files('capstone.c')) diff --git a/disas/sw64.c b/disas/sw64.c new file mode 100755 index 0000000000..c5bd578e07 --- /dev/null +++ b/disas/sw64.c @@ -0,0 +1,1242 @@ +/* + * sw_64-dis.c -- Disassemble Sw_64 CORE3 instructions + * + * This file is part of libopcodes. + * + * This library 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 3, or (at your option) + * any later version. + * + * It 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 file; see the file COPYING. If not, write to the Free + * Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "qemu/osdep.h" +#include "disas/dis-asm.h" + +#undef MAX + +struct sw_64_opcode { + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned mask; + + /* One bit flags for the opcode. These are primarily used to + indicate specific processors and environments support the + instructions. The defined values are listed below. */ + unsigned flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[5]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct sw_64_opcode sw_64_opcodes[]; +extern const unsigned sw_64_num_opcodes; + +/* Values defined for the flags field of a struct sw_64_opcode. */ + +/* CPU Availability */ +#define SW_OPCODE_BASE 0x0001 /* Base architecture insns. */ +#define SW_OPCODE_CORE3 0x0002 /* Core3 private insns. */ +#define SW_LITOP(i) (((i) >> 26) & 0x3D) + +#define SW_OPCODE_NOHM (~(SW_OPCODE_BASE|SW_OPCODE_CORE3)) + +/* A macro to extract the major opcode from an instruction. */ +#define SW_OP(i) (((i) >> 26) & 0x3F) + +/* The total number of major opcodes. */ +#define SW_NOPS 0x40 + +/* The operands table is an array of struct sw_64_operand. */ + +struct sw_64_operand { + /* The number of bits in the operand. */ + unsigned int bits : 5; + + /* How far the operand is left shifted in the instruction. */ + unsigned int shift : 5; + + /* The default relocation type for this operand. */ + signed int default_reloc : 16; + + /* One bit syntax flags. */ + unsigned int flags : 16; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned (*insert) (unsigned instruction, int op, const char **errmsg); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & SW_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + int (*extract) (unsigned instruction, int *invalid); +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the sw_64_opcodes table. */ + +extern const struct sw_64_operand sw_64_operands[]; +extern const unsigned sw_64_num_operands; +/* Values defined for the flags field of a struct sw_64_operand. */ + +/* Mask for selecting the type for typecheck purposes */ +#define SW_OPERAND_TYPECHECK_MASK \ + (SW_OPERAND_PARENS | SW_OPERAND_COMMA | SW_OPERAND_IR | \ + SW_OPERAND_FPR | SW_OPERAND_RELATIVE | SW_OPERAND_SIGNED | \ + SW_OPERAND_UNSIGNED) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics, for which two operands fields + are identical. The assembler should call the insert function with + any op value. The disassembler should call the extract function, + ignore the return value, and check the value placed in the invalid + argument. */ +#define SW_OPERAND_FAKE 01 + +/* The operand should be wrapped in parentheses rather than separated + from the previous by a comma. This is used for the load and store + instructions which want their operands to look like "Ra,disp(Rb)". */ +#define SW_OPERAND_PARENS 02 + +/* Used in combination with PARENS, this supresses the supression of + the comma. This is used for "jmp Ra,(Rb),hint". */ +#define SW_OPERAND_COMMA 04 + +/* This operand names an integer register. */ +#define SW_OPERAND_IR 010 + +/* This operand names a floating point register. */ +#define SW_OPERAND_FPR 020 + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define SW_OPERAND_RELATIVE 040 + +/* This operand takes signed values. */ +#define SW_OPERAND_SIGNED 0100 + +/* This operand takes unsigned values. This exists primarily so that + a flags value of 0 can be treated as end-of-arguments. */ +#define SW_OPERAND_UNSIGNED 0200 + +/* Supress overflow detection on this field. This is used for hints. */ +#define SW_OPERAND_NOOVERFLOW 0400 + +/* Mask for optional argument default value. */ +#define SW_OPERAND_OPTIONAL_MASK 07000 + +/* This operand defaults to zero. This is used for jump hints. */ +#define SW_OPERAND_DEFAULT_ZERO 01000 + +/* This operand should default to the first (real) operand and is used + in conjunction with SW_OPERAND_OPTIONAL. This allows + "and $0,3,$0" to be written as "and $0,3", etc. I don't like + it, but it's what DEC does. */ +#define SW_OPERAND_DEFAULT_FIRST 02000 + +/* Similarly, this operand should default to the second (real) operand. + This allows "negl $0" instead of "negl $0,$0". */ +#define SW_OPERAND_DEFAULT_SECOND 04000 + +/* Register common names */ + +#define SW_REG_V0 0 +#define SW_REG_T0 1 +#define SW_REG_T1 2 +#define SW_REG_T2 3 +#define SW_REG_T3 4 +#define SW_REG_T4 5 +#define SW_REG_T5 6 +#define SW_REG_T6 7 +#define SW_REG_T7 8 +#define SW_REG_S0 9 +#define SW_REG_S1 10 +#define SW_REG_S2 11 +#define SW_REG_S3 12 +#define SW_REG_S4 13 +#define SW_REG_S5 14 +#define SW_REG_FP 15 +#define SW_REG_A0 16 +#define SW_REG_A1 17 +#define SW_REG_A2 18 +#define SW_REG_A3 19 +#define SW_REG_A4 20 +#define SW_REG_A5 21 +#define SW_REG_T8 22 +#define SW_REG_T9 23 +#define SW_REG_T10 24 +#define SW_REG_T11 25 +#define SW_REG_RA 26 +#define SW_REG_PV 27 +#define SW_REG_T12 27 +#define SW_REG_AT 28 +#define SW_REG_GP 29 +#define SW_REG_SP 30 +#define SW_REG_ZERO 31 + +enum bfd_reloc_code_real { + BFD_RELOC_23_PCREL_S2, + BFD_RELOC_SW_64_HINT +}; + +static unsigned insert_rba(unsigned insn, int value ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static int extract_rba(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The same for the RC field. */ +static unsigned insert_rca(unsigned insn, int value ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | ((insn >> 21) & 0x1f); +} + +static unsigned insert_rdc(unsigned insn, int value ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | ((insn >> 5) & 0x1f); +} + +static int extract_rdc(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 5) & 0x1f) != (insn & 0x1f)) + *invalid = 1; + return 0; +} + +static int extract_rca(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != (insn & 0x1f)) + *invalid = 1; + return 0; +} + +/* Fake arguments in which the registers must be set to ZERO. */ +static unsigned insert_za(unsigned insn, int value ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (31 << 21); +} + +static int extract_za(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL && ((insn >> 21) & 0x1f) != 31) + *invalid = 1; + return 0; +} + +static unsigned insert_zb(unsigned insn, int value ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | (31 << 16); +} + +static int extract_zb(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL && ((insn >> 16) & 0x1f) != 31) + *invalid = 1; + return 0; +} + +static unsigned insert_zc(unsigned insn, int value ATTRIBUTE_UNUSED, + const char **errmsg ATTRIBUTE_UNUSED) +{ + return insn | 31; +} + +static int extract_zc(unsigned insn, int *invalid) +{ + if (invalid != (int *) NULL && (insn & 0x1f) != 31) + *invalid = 1; + return 0; +} + + +/* The displacement field of a Branch format insn. */ + +static unsigned insert_bdisp(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = "branch operand unaligned"; + return insn | ((value / 4) & 0x1FFFFF); +} + +static int extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0x1FFFFF) ^ 0x100000) - 0x100000); +} + +static unsigned insert_bdisp26(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = "branch operand unaligned"; + return insn | ((value / 4) & 0x3FFFFFF); +} + +static int extract_bdisp26(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0x3FFFFFF) ^ 0x2000000) - 0x2000000); +} + +/* The hint field of a JMP/JSR insn. */ +/* sw use 16 bits hint disp. */ +static unsigned insert_jhint(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = "jump hint unaligned"; + return insn | ((value / 4) & 0xFFFF); +} + +static int extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0xFFFF) ^ 0x8000) - 0x8000); +} + +/* The hint field of an CORE3 HW_JMP/JSR insn. */ + +static unsigned insert_sw4hwjhint(unsigned insn, int value, const char **errmsg) +{ + if (errmsg != (const char **)NULL && (value & 3)) + *errmsg = "jump hint unaligned"; + return insn | ((value / 4) & 0x1FFF); +} + +static int extract_sw4hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED) +{ + return 4 * (((insn & 0x1FFF) ^ 0x1000) - 0x1000); +} + +/* The operands table. */ + +const struct sw_64_operand sw_64_operands[] = { + /* The fields are bits, shift, insert, extract, flags */ + /* The zero index is used to indicate end-of-list */ +#define UNUSED 0 + { 0, 0, 0, 0, 0, 0 }, + + /* The plain integer register fields. */ +#define RA (UNUSED + 1) + { 5, 21, 0, SW_OPERAND_IR, 0, 0 }, +#define RB (RA + 1) + { 5, 16, 0, SW_OPERAND_IR, 0, 0 }, +#define RC (RB + 1) + { 5, 0, 0, SW_OPERAND_IR, 0, 0 }, + + /* The plain fp register fields. */ +#define FA (RC + 1) + { 5, 21, 0, SW_OPERAND_FPR, 0, 0 }, +#define FB (FA + 1) + { 5, 16, 0, SW_OPERAND_FPR, 0, 0 }, +#define FC (FB + 1) + { 5, 0, 0, SW_OPERAND_FPR, 0, 0 }, + + /* The integer registers when they are ZERO. */ +#define ZA (FC + 1) + { 5, 21, 0, SW_OPERAND_FAKE, insert_za, extract_za }, +#define ZB (ZA + 1) + { 5, 16, 0, SW_OPERAND_FAKE, insert_zb, extract_zb }, +#define ZC (ZB + 1) + { 5, 0, 0, SW_OPERAND_FAKE, insert_zc, extract_zc }, + + /* The RB field when it needs parentheses. */ +#define PRB (ZC + 1) + { 5, 16, 0, SW_OPERAND_IR | SW_OPERAND_PARENS, 0, 0 }, + + /* The RB field when it needs parentheses _and_ a preceding comma. */ +#define CPRB (PRB + 1) + { 5, 16, 0, + SW_OPERAND_IR | SW_OPERAND_PARENS | SW_OPERAND_COMMA, 0, 0 }, + + /* The RB field when it must be the same as the RA field. */ +#define RBA (CPRB + 1) + { 5, 16, 0, SW_OPERAND_FAKE, insert_rba, extract_rba }, + + /* The RC field when it must be the same as the RB field. */ +#define RCA (RBA + 1) + { 5, 0, 0, SW_OPERAND_FAKE, insert_rca, extract_rca }, + +#define RDC (RCA + 1) + { 5, 0, 0, SW_OPERAND_FAKE, insert_rdc, extract_rdc }, + + /* The RC field when it can *default* to RA. */ +#define DRC1 (RDC + 1) + { 5, 0, 0, + SW_OPERAND_IR | SW_OPERAND_DEFAULT_FIRST, 0, 0 }, + + /* The RC field when it can *default* to RB. */ +#define DRC2 (DRC1 + 1) + { 5, 0, 0, + SW_OPERAND_IR | SW_OPERAND_DEFAULT_SECOND, 0, 0 }, + + /* The FC field when it can *default* to RA. */ +#define DFC1 (DRC2 + 1) + { 5, 0, 0, + SW_OPERAND_FPR | SW_OPERAND_DEFAULT_FIRST, 0, 0 }, + + /* The FC field when it can *default* to RB. */ +#define DFC2 (DFC1 + 1) + { 5, 0, 0, + SW_OPERAND_FPR | SW_OPERAND_DEFAULT_SECOND, 0, 0 }, + + /* The unsigned 8-bit literal of Operate format insns. */ +#define LIT (DFC2 + 1) + { 8, 13, -LIT, SW_OPERAND_UNSIGNED, 0, 0 }, + + /* The signed 16-bit displacement of Memory format insns. From here + we can't tell what relocation should be used, so don't use a default. */ +#define MDISP (LIT + 1) + { 16, 0, -MDISP, SW_OPERAND_SIGNED, 0, 0 }, + + /* The signed "23-bit" aligned displacement of Branch format insns. */ +#define BDISP (MDISP + 1) + { 21, 0, BFD_RELOC_23_PCREL_S2, + SW_OPERAND_RELATIVE, insert_bdisp, extract_bdisp }, + + /* The 26-bit hmcode function for sys_call and sys_call / b. */ +#define HMFN (BDISP + 1) + { 25, 0, -HMFN, SW_OPERAND_UNSIGNED, 0, 0 }, + + /* sw jsr/ret insntructions has no function bits. */ + /* The optional signed "16-bit" aligned displacement of the JMP/JSR hint. */ +#define JMPHINT (HMFN + 1) + { 16, 0, BFD_RELOC_SW_64_HINT, + SW_OPERAND_RELATIVE | SW_OPERAND_DEFAULT_ZERO | SW_OPERAND_NOOVERFLOW, + insert_jhint, extract_jhint }, + + /* The optional hint to RET/JSR_COROUTINE. */ +#define RETHINT (JMPHINT + 1) + { 16, 0, -RETHINT, + SW_OPERAND_UNSIGNED | SW_OPERAND_DEFAULT_ZERO, 0, 0 }, + + /* The 12-bit displacement for the core3 hw_{ld,st} (pal1b/pal1f) insns. */ +#define HWDISP (RETHINT + 1) + { 12, 0, -HWDISP, SW_OPERAND_SIGNED, 0, 0 }, + + /* The 16-bit combined index/scoreboard mask for the core3 + hw_m[ft]pr (pal19/pal1d) insns. */ +#define HWINDEX (HWDISP + 1) + { 16, 0, -HWINDEX, SW_OPERAND_UNSIGNED, 0, 0 }, + + /* The 13-bit branch hint for the core3 hw_jmp/jsr (pal1e) insn. */ +#define HWJMPHINT (HWINDEX + 1) + { 8, 0, -HWJMPHINT, + SW_OPERAND_RELATIVE | SW_OPERAND_DEFAULT_ZERO | SW_OPERAND_NOOVERFLOW, + insert_sw4hwjhint, extract_sw4hwjhint }, + + /* for the third operand of ternary operands integer insn. */ +#define R3 (HWJMPHINT + 1) + { 5, 5, 0, SW_OPERAND_IR, 0, 0 }, + /* The plain fp register fields */ +#define F3 (R3 + 1) + { 5, 5, 0, SW_OPERAND_FPR, 0, 0 }, + /* sw simd settle instruction lit */ +#define FMALIT (F3 + 1) + { 5, 5, -FMALIT, SW_OPERAND_UNSIGNED, 0, 0 }, //V1.1 +#define LMDISP (FMALIT + 1) + { 15, 0, -LMDISP, SW_OPERAND_UNSIGNED, 0, 0 }, +#define RPIINDEX (LMDISP + 1) + { 8, 0, -RPIINDEX, SW_OPERAND_UNSIGNED, 0, 0 }, +#define ATMDISP (RPIINDEX + 1) + { 12, 0, -ATMDISP, SW_OPERAND_SIGNED, 0, 0 }, +#define DISP13 (ATMDISP + 1) + { 13, 13, -DISP13, SW_OPERAND_SIGNED, 0, 0}, +#define BDISP26 (DISP13 + 1) + { 26, 0, 222, + SW_OPERAND_RELATIVE, insert_bdisp26, extract_bdisp26 }, +#define DPFTH (BDISP26 + 1) + { 5, 21, -DPFTH, SW_OPERAND_UNSIGNED, 0, 0} +}; + +const unsigned sw_64_num_operands = sizeof(sw_64_operands) / sizeof(*sw_64_operands); + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) (((x) & 0x3F) << 26) +#define OP_MASK 0xFC000000 + +/* Branch format instructions. */ +#define BRA_(oo) OP(oo) +#define BRA_MASK OP_MASK +#define BRA(oo) BRA_(oo), BRA_MASK + +#ifdef HUANGLM20171113 +/* Floating point format instructions. */ +#define FP_(oo,fff) (OP(oo) | (((fff) & 0x7FF) << 5)) +#define FP_MASK (OP_MASK | 0xFFE0) +#define FP(oo,fff) FP_(oo,fff), FP_MASK + +#else +/* Floating point format instructions. */ +#define FP_(oo,fff) (OP(oo) | (((fff) & 0xFF) << 5)) +#define FP_MASK (OP_MASK | 0x1FE0) +#define FP(oo,fff) FP_(oo,fff), FP_MASK + +#define FMA_(oo,fff) (OP(oo) | (((fff) & 0x3F) << 10 )) +#define FMA_MASK (OP_MASK | 0xFC00) +#define FMA(oo,fff) FMA_(oo,fff), FMA_MASK +#endif + +/* Memory format instructions. */ +#define MEM_(oo) OP(oo) +#define MEM_MASK OP_MASK +#define MEM(oo) MEM_(oo), MEM_MASK + +/* Memory/Func Code format instructions. */ +#define MFC_(oo,ffff) (OP(oo) | ((ffff) & 0xFFFF)) +#define MFC_MASK (OP_MASK | 0xFFFF) +#define MFC(oo,ffff) MFC_(oo,ffff), MFC_MASK + +/* Memory/Branch format instructions. */ +#define MBR_(oo,h) (OP(oo) | (((h) & 3) << 14)) +#define MBR_MASK (OP_MASK | 0xC000) +#define MBR(oo,h) MBR_(oo,h), MBR_MASK + +/* Now sw Operate format instructions is different with SW1. */ +#define OPR_(oo,ff) (OP(oo) | (((ff) & 0xFF) << 5)) +#define OPRL_(oo,ff) (OPR_((oo), (ff)) ) +#define OPR_MASK (OP_MASK | 0x1FE0) +#define OPR(oo,ff) OPR_(oo,ff), OPR_MASK +#define OPRL(oo,ff) OPRL_(oo,ff), OPR_MASK + +/* sw ternary operands Operate format instructions. */ +#define TOPR_(oo,ff) (OP(oo) | (((ff) & 0x07) << 10)) +#define TOPRL_(oo,ff) (TOPR_((oo), (ff))) +#define TOPR_MASK (OP_MASK | 0x1C00) +#define TOPR(oo,ff) TOPR_(oo,ff), TOPR_MASK +#define TOPRL(oo,ff) TOPRL_(oo,ff), TOPR_MASK + +/* sw atom instructions. */ +#define ATMEM_(oo,h) (OP(oo) | (((h) & 0xF) << 12)) +#define ATMEM_MASK (OP_MASK | 0xF000) +#define ATMEM(oo,h) ATMEM_(oo,h), ATMEM_MASK + +/* sw privilege instructions. */ +#define PRIRET_(oo,h) (OP(oo) | (((h) & 0x1) << 20)) +#define PRIRET_MASK (OP_MASK | 0x100000) +#define PRIRET(oo,h) PRIRET_(oo,h), PRIRET_MASK + +/* sw rpi_rcsr,rpi_wcsr. */ +#define CSR_(oo,ff) (OP(oo) | (((ff) & 0xFF) << 8)) +#define CSR_MASK (OP_MASK | 0xFF00) +#define CSR(oo,ff) CSR_(oo,ff), CSR_MASK + +#define PCD_(oo,ff) (OP(oo) | (ff << 25)) +#define PCD_MASK OP_MASK +#define PCD(oo,ff) PCD_(oo,ff), PCD_MASK + +/* Hardware memory (hw_{ld,st}) instructions. */ +#define HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12)) +#define HWMEM_MASK (OP_MASK | 0xF000) +#define HWMEM(oo,f) HWMEM_(oo,f), HWMEM_MASK + +#define LOGX_(oo,ff) (OP(oo) | (((ff) & 0x3F) << 10)) +#define LOGX_MASK (0xF0000000) +#define LOGX(oo,ff) LOGX_(oo,ff), LOGX_MASK + +/* Abbreviations for instruction subsets. */ +#define BASE SW_OPCODE_BASE +#define CORE3 SW_OPCODE_CORE3 + +/* Common combinations of arguments. */ +#define ARG_NONE { 0 } +#define ARG_BRA { RA, BDISP } +#define ARG_FBRA { FA, BDISP } +#define ARG_FP { FA, FB, DFC1 } +#define ARG_FPZ1 { ZA, FB, DFC1 } +#define ARG_MEM { RA, MDISP, PRB } +#define ARG_FMEM { FA, MDISP, PRB } +#define ARG_OPR { RA, RB, DRC1 } + +#define ARG_OPRCAS { RA, RB, RC } + +#define ARG_OPRL { RA, LIT, DRC1 } +#define ARG_OPRZ1 { ZA, RB, DRC1 } +#define ARG_OPRLZ1 { ZA, LIT, RC } +#define ARG_PCD { HMFN } +#define ARG_HWMEM { RA, HWDISP, PRB } +#define ARG_FPL { FA,LIT, DFC1 } +#define ARG_FMA { FA,FB,F3, DFC1 } +#define ARG_PREFETCH { ZA, MDISP, PRB } +#define ARG_TOPR { RA, RB,R3, DRC1 } +#define ARG_TOPRL { RA, LIT, R3,DRC1 } +#define ARG_FMAL { FA,FB,FMALIT, DFC1 } +#define ARG_ATMEM { RA, ATMDISP, PRB } +#define ARG_VUAMEM { FA, ATMDISP, PRB } +#define ARG_OPRLZ3 { RA, LIT, ZC } + +#define ARG_DISP13 {DISP13, RC} + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK { OPERANDS } + + NAME is the name of the instruction. + + OPCODE is the instruction opcode. + + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + + OPERANDS is the list of operands. + + The preceding macros merge the text of the OPCODE and MASK fields. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. + + Otherwise, it is sorted by major opcode and minor function code. + */ + +const struct sw_64_opcode sw_64_opcodes[] = { + { "sys_call/b", PCD(0x00,0x00), BASE, ARG_PCD }, + { "sys_call", PCD(0x00,0x01), BASE, ARG_PCD }, + + { "call", MEM(0x01), BASE, { RA, CPRB, JMPHINT } }, + { "ret", MEM(0x02), BASE, { RA, CPRB, RETHINT } }, + { "jmp", MEM(0x03), BASE, { RA, CPRB, JMPHINT } }, + { "br", BRA(0x04), BASE, { ZA, BDISP } }, + { "br", BRA(0x04), BASE, ARG_BRA }, + { "bsr", BRA(0x05), BASE, ARG_BRA }, + { "memb", MFC(0x06,0x0000), BASE, ARG_NONE }, + { "imemb", MFC(0x06,0x0001), BASE, ARG_NONE }, + { "rtc", MFC(0x06,0x0020), BASE, { RA, ZB } }, + { "rtc", MFC(0x06,0x0020), BASE, { RA, RB } }, + { "rcid", MFC(0x06,0x0040), BASE, { RA , ZB} }, + { "halt", MFC(0x06,0x0080), BASE, { ZA, ZB } }, + { "rd_f", MFC(0x06,0x1000), CORE3, { RA, ZB } }, + { "wr_f", MFC(0x06,0x1020), CORE3, { RA, ZB } }, + { "rtid", MFC(0x06,0x1040), BASE, { RA } }, + { "pri_rcsr", CSR(0x06,0xFE), CORE3, { RA, RPIINDEX ,ZB } }, + { "pri_wcsr", CSR(0x06,0xFF), CORE3, { RA, RPIINDEX ,ZB } }, + { "pri_ret", PRIRET(0x07,0x0), BASE, { RA } }, + { "pri_ret/b", PRIRET(0x07,0x1), BASE, { RA } }, + { "lldw", ATMEM(0x08,0x0), BASE, ARG_ATMEM }, + { "lldl", ATMEM(0x08,0x1), BASE, ARG_ATMEM }, + { "ldw_inc", ATMEM(0x08,0x2), CORE3, ARG_ATMEM }, + { "ldl_inc", ATMEM(0x08,0x3), CORE3, ARG_ATMEM }, + { "ldw_dec", ATMEM(0x08,0x4), CORE3, ARG_ATMEM }, + { "ldl_dec", ATMEM(0x08,0x5), CORE3, ARG_ATMEM }, + { "ldw_set", ATMEM(0x08,0x6), CORE3, ARG_ATMEM }, + { "ldl_set", ATMEM(0x08,0x7), CORE3, ARG_ATMEM }, + { "lstw", ATMEM(0x08,0x8), BASE, ARG_ATMEM }, + { "lstl", ATMEM(0x08,0x9), BASE, ARG_ATMEM }, + { "ldw_nc", ATMEM(0x08,0xA), BASE, ARG_ATMEM }, + { "ldl_nc", ATMEM(0x08,0xB), BASE, ARG_ATMEM }, + { "ldd_nc", ATMEM(0x08,0xC), BASE, ARG_VUAMEM }, + { "stw_nc", ATMEM(0x08,0xD), BASE, ARG_ATMEM }, + { "stl_nc", ATMEM(0x08,0xE), BASE, ARG_ATMEM }, + { "std_nc", ATMEM(0x08,0xF), BASE, ARG_VUAMEM }, + { "fillcs", MEM(0x09), BASE, ARG_PREFETCH }, + { "ldwe", MEM(0x09), BASE, ARG_FMEM }, + { "e_fillcs", MEM(0x0A), BASE, ARG_PREFETCH }, + { "ldse", MEM(0x0A), BASE, ARG_FMEM }, + { "fillcs_e", MEM(0x0B), BASE, ARG_PREFETCH }, + { "ldde", MEM(0x0B), BASE, ARG_FMEM }, + { "vlds", MEM(0x0C), BASE, ARG_FMEM }, + { "vldd", MEM(0x0D), BASE, ARG_FMEM }, + { "vsts", MEM(0x0E), BASE, ARG_FMEM }, + { "vstd", MEM(0x0F), BASE, ARG_FMEM }, + { "addw", OPR(0x10,0x00), BASE, ARG_OPR }, + { "addw", OPRL(0x12,0x00), BASE, ARG_OPRL }, + { "subw", OPR(0x10,0x01), BASE, ARG_OPR }, + { "subw", OPRL(0x12,0x01), BASE, ARG_OPRL }, + { "s4addw", OPR(0x10,0x02), BASE, ARG_OPR }, + { "s4addw", OPRL(0x12,0x02), BASE, ARG_OPRL }, + { "s4subw", OPR(0x10,0x03), BASE, ARG_OPR }, + { "s4subw", OPRL(0x12,0x03), BASE, ARG_OPRL }, + { "s8addw", OPR(0x10,0x04), BASE, ARG_OPR }, + { "s8addw", OPRL(0x12,0x04), BASE, ARG_OPRL }, + { "s8subw", OPR(0x10,0x05), BASE, ARG_OPR }, + { "s8subw", OPRL(0x12,0x05), BASE, ARG_OPRL }, + { "addl", OPR(0x10,0x08), BASE, ARG_OPR }, + { "addl", OPRL(0x12,0x08), BASE, ARG_OPRL }, + { "subl", OPR(0x10,0x09), BASE, ARG_OPR }, + { "subl", OPRL(0x12,0x09), BASE, ARG_OPRL }, + { "s4addl", OPR(0x10,0x0A), BASE, ARG_OPR }, + { "s4addl", OPRL(0x12,0x0A), BASE, ARG_OPRL }, + { "s4subl", OPR(0x10,0x0B), BASE, ARG_OPR }, + { "s4subl", OPRL(0x12,0x0B), BASE, ARG_OPRL }, + { "s8addl", OPR(0x10,0x0C), BASE, ARG_OPR }, + { "s8addl", OPRL(0x12,0x0C), BASE, ARG_OPRL }, + { "s8subl", OPR(0x10,0x0D), BASE, ARG_OPR }, + { "s8subl", OPRL(0x12,0x0D), BASE, ARG_OPRL }, + { "mulw", OPR(0x10,0x10), BASE, ARG_OPR }, + { "mulw", OPRL(0x12,0x10), BASE, ARG_OPRL }, + { "mull", OPR(0x10,0x18), BASE, ARG_OPR }, + { "mull", OPRL(0x12,0x18), BASE, ARG_OPRL }, + { "umulh", OPR(0x10,0x19), BASE, ARG_OPR }, + { "umulh", OPRL(0x12,0x19), BASE, ARG_OPRL }, + { "cmpeq", OPR(0x10,0x28), BASE, ARG_OPR }, + { "cmpeq", OPRL(0x12,0x28), BASE, ARG_OPRL }, + { "cmplt", OPR(0x10,0x29), BASE, ARG_OPR }, + { "cmplt", OPRL(0x12,0x29), BASE, ARG_OPRL }, + { "cmple", OPR(0x10,0x2A), BASE, ARG_OPR }, + { "cmple", OPRL(0x12,0x2A), BASE, ARG_OPRL }, + { "cmpult", OPR(0x10,0x2B), BASE, ARG_OPR }, + { "cmpult", OPRL(0x12,0x2B), BASE, ARG_OPRL }, + { "cmpule", OPR(0x10,0x2C), BASE, ARG_OPR }, + { "cmpule", OPRL(0x12,0x2C), BASE, ARG_OPRL }, + + { "and", OPR(0x10,0x38), BASE, ARG_OPR }, + { "and", OPRL(0x12,0x38),BASE, ARG_OPRL }, + { "bic", OPR(0x10,0x39), BASE, ARG_OPR }, + { "bic", OPRL(0x12,0x39),BASE, ARG_OPRL }, + { "bis", OPR(0x10,0x3A), BASE, ARG_OPR }, + { "bis", OPRL(0x12,0x3A),BASE, ARG_OPRL }, + { "ornot", OPR(0x10,0x3B), BASE, ARG_OPR }, + { "ornot", OPRL(0x12,0x3B),BASE, ARG_OPRL }, + { "xor", OPR(0x10,0x3C), BASE, ARG_OPR }, + { "xor", OPRL(0x12,0x3C),BASE, ARG_OPRL }, + { "eqv", OPR(0x10,0x3D), BASE, ARG_OPR }, + { "eqv", OPRL(0x12,0x3D),BASE, ARG_OPRL }, + { "inslb", OPR(0x10,0x40), BASE, ARG_OPR }, + { "inslb", OPRL(0x12,0x40),BASE, ARG_OPRL }, + { "inslh", OPR(0x10,0x41), BASE, ARG_OPR }, + { "inslh", OPRL(0x12,0x41),BASE, ARG_OPRL }, + { "inslw", OPR(0x10,0x42), BASE, ARG_OPR }, + { "inslw", OPRL(0x12,0x42),BASE, ARG_OPRL }, + { "insll", OPR(0x10,0x43), BASE, ARG_OPR }, + { "insll", OPRL(0x12,0x43),BASE, ARG_OPRL }, + { "inshb", OPR(0x10,0x44), BASE, ARG_OPR }, + { "inshb", OPRL(0x12,0x44),BASE, ARG_OPRL }, + { "inshh", OPR(0x10,0x45), BASE, ARG_OPR }, + { "inshh", OPRL(0x12,0x45),BASE, ARG_OPRL }, + { "inshw", OPR(0x10,0x46), BASE, ARG_OPR }, + { "inshw", OPRL(0x12,0x46),BASE, ARG_OPRL }, + { "inshl", OPR(0x10,0x47), BASE, ARG_OPR }, + { "inshl", OPRL(0x12,0x47),BASE, ARG_OPRL }, + + { "sll", OPR(0x10,0x48), BASE, ARG_OPR }, + { "sll", OPRL(0x12,0x48),BASE, ARG_OPRL }, + { "srl", OPR(0x10,0x49), BASE, ARG_OPR }, + { "srl", OPRL(0x12,0x49),BASE, ARG_OPRL }, + { "sra", OPR(0x10,0x4A), BASE, ARG_OPR }, + { "sra", OPRL(0x12,0x4A),BASE, ARG_OPRL }, + { "extlb", OPR(0x10,0x50), BASE, ARG_OPR }, + { "extlb", OPRL(0x12,0x50),BASE, ARG_OPRL }, + { "extlh", OPR(0x10,0x51), BASE, ARG_OPR }, + { "extlh", OPRL(0x12,0x51),BASE, ARG_OPRL }, + { "extlw", OPR(0x10,0x52), BASE, ARG_OPR }, + { "extlw", OPRL(0x12,0x52),BASE, ARG_OPRL }, + { "extll", OPR(0x10,0x53), BASE, ARG_OPR }, + { "extll", OPRL(0x12,0x53),BASE, ARG_OPRL }, + { "exthb", OPR(0x10,0x54), BASE, ARG_OPR }, + { "exthb", OPRL(0x12,0x54),BASE, ARG_OPRL }, + { "exthh", OPR(0x10,0x55), BASE, ARG_OPR }, + { "exthh", OPRL(0x12,0x55),BASE, ARG_OPRL }, + { "exthw", OPR(0x10,0x56), BASE, ARG_OPR }, + { "exthw", OPRL(0x12,0x56),BASE, ARG_OPRL }, + { "exthl", OPR(0x10,0x57), BASE, ARG_OPR }, + { "exthl", OPRL(0x12,0x57),BASE, ARG_OPRL }, + { "ctpop", OPR(0x10,0x58), BASE, ARG_OPRZ1 }, + { "ctlz", OPR(0x10,0x59), BASE, ARG_OPRZ1 }, + { "cttz", OPR(0x10,0x5A), BASE, ARG_OPRZ1 }, + { "masklb", OPR(0x10,0x60), BASE, ARG_OPR }, + { "masklb", OPRL(0x12,0x60),BASE, ARG_OPRL }, + { "masklh", OPR(0x10,0x61), BASE, ARG_OPR }, + { "masklh", OPRL(0x12,0x61),BASE, ARG_OPRL }, + { "masklw", OPR(0x10,0x62), BASE, ARG_OPR }, + { "masklw", OPRL(0x12,0x62),BASE, ARG_OPRL }, + { "maskll", OPR(0x10,0x63), BASE, ARG_OPR }, + { "maskll", OPRL(0x12,0x63),BASE, ARG_OPRL }, + { "maskhb", OPR(0x10,0x64), BASE, ARG_OPR }, + { "maskhb", OPRL(0x12,0x64),BASE, ARG_OPRL }, + { "maskhh", OPR(0x10,0x65), BASE, ARG_OPR }, + { "maskhh", OPRL(0x12,0x65),BASE, ARG_OPRL }, + { "maskhw", OPR(0x10,0x66), BASE, ARG_OPR }, + { "maskhw", OPRL(0x12,0x66),BASE, ARG_OPRL }, + { "maskhl", OPR(0x10,0x67), BASE, ARG_OPR }, + { "maskhl", OPRL(0x12,0x67),BASE, ARG_OPRL }, + { "zap", OPR(0x10,0x68), BASE, ARG_OPR }, + { "zap", OPRL(0x12,0x68),BASE, ARG_OPRL }, + { "zapnot", OPR(0x10,0x69), BASE, ARG_OPR }, + { "zapnot", OPRL(0x12,0x69),BASE, ARG_OPRL }, + { "sextb", OPR(0x10,0x6A), BASE, ARG_OPRZ1}, + { "sextb", OPRL(0x12,0x6A),BASE, ARG_OPRLZ1 }, + { "sexth", OPR(0x10,0x6B), BASE, ARG_OPRZ1 }, + { "sexth", OPRL(0x12,0x6B),BASE, ARG_OPRLZ1 }, + { "cmpgeb", OPR(0x10,0x6C), BASE, ARG_OPR }, + { "cmpgeb", OPRL(0x12,0x6C),BASE, ARG_OPRL }, + { "fimovs", OPR(0x10,0x70), BASE, { FA, ZB, RC } }, + { "fimovd", OPR(0x10,0x78), BASE, { FA, ZB, RC } }, + { "seleq", TOPR(0x11,0x0), BASE, ARG_TOPR }, + { "seleq", TOPRL(0x13,0x0),BASE, ARG_TOPRL }, + { "selge", TOPR(0x11,0x1), BASE, ARG_TOPR }, + { "selge", TOPRL(0x13,0x1),BASE, ARG_TOPRL }, + { "selgt", TOPR(0x11,0x2), BASE, ARG_TOPR }, + { "selgt", TOPRL(0x13,0x2),BASE, ARG_TOPRL }, + { "selle", TOPR(0x11,0x3), BASE, ARG_TOPR }, + { "selle", TOPRL(0x13,0x3),BASE, ARG_TOPRL }, + { "sellt", TOPR(0x11,0x4), BASE, ARG_TOPR }, + { "sellt", TOPRL(0x13,0x4),BASE, ARG_TOPRL }, + { "selne", TOPR(0x11,0x5), BASE, ARG_TOPR }, + { "selne", TOPRL(0x13,0x5),BASE, ARG_TOPRL }, + { "sellbc", TOPR(0x11,0x6), BASE, ARG_TOPR }, + { "sellbc", TOPRL(0x13,0x6),BASE, ARG_TOPRL }, + { "sellbs", TOPR(0x11,0x7), BASE, ARG_TOPR }, + { "sellbs", TOPRL(0x13,0x7),BASE, ARG_TOPRL }, + { "vlog", LOGX(0x14,0x00), BASE, ARG_FMA }, + + { "fadds", FP(0x18,0x00), BASE, ARG_FP }, + { "faddd", FP(0x18,0x01), BASE, ARG_FP }, + { "fsubs", FP(0x18,0x02), BASE, ARG_FP }, + { "fsubd", FP(0x18,0x03), BASE, ARG_FP }, + { "fmuls", FP(0x18,0x04), BASE, ARG_FP }, + { "fmuld", FP(0x18,0x05), BASE, ARG_FP }, + { "fdivs", FP(0x18,0x06), BASE, ARG_FP }, + { "fdivd", FP(0x18,0x07), BASE, ARG_FP }, + { "fsqrts", FP(0x18,0x08), BASE, ARG_FPZ1 }, + { "fsqrtd", FP(0x18,0x09), BASE, ARG_FPZ1 }, + { "fcmpeq", FP(0x18,0x10), BASE, ARG_FP }, + { "fcmple", FP(0x18,0x11), BASE, ARG_FP }, + { "fcmplt", FP(0x18,0x12), BASE, ARG_FP }, + { "fcmpun", FP(0x18,0x13), BASE, ARG_FP }, + + { "fcvtsd", FP(0x18,0x20), BASE, ARG_FPZ1 }, + { "fcvtds", FP(0x18,0x21), BASE, ARG_FPZ1 }, + { "fcvtdl_g", FP(0x18,0x22), BASE, ARG_FPZ1 }, + { "fcvtdl_p", FP(0x18,0x23), BASE, ARG_FPZ1 }, + { "fcvtdl_z", FP(0x18,0x24), BASE, ARG_FPZ1 }, + { "fcvtdl_n", FP(0x18,0x25), BASE, ARG_FPZ1 }, + { "fcvtdl", FP(0x18,0x27), BASE, ARG_FPZ1 }, + { "fcvtwl", FP(0x18,0x28), BASE, ARG_FPZ1 }, + { "fcvtlw", FP(0x18,0x29), BASE, ARG_FPZ1 }, + { "fcvtls", FP(0x18,0x2d), BASE, ARG_FPZ1 }, + { "fcvtld", FP(0x18,0x2f), BASE, ARG_FPZ1 }, + { "fcpys", FP(0x18,0x30), BASE, ARG_FP }, + { "fcpyse", FP(0x18,0x31), BASE, ARG_FP }, + { "fcpysn", FP(0x18,0x32), BASE, ARG_FP }, + { "ifmovs", FP(0x18,0x40), BASE, { RA, ZB, FC } }, + { "ifmovd", FP(0x18,0x41), BASE, { RA, ZB, FC } }, + { "rfpcr", FP(0x18,0x50), BASE, { FA, RBA, RCA } }, + { "wfpcr", FP(0x18,0x51), BASE, { FA, RBA, RCA } }, + { "setfpec0", FP(0x18,0x54), BASE, ARG_NONE }, + { "setfpec1", FP(0x18,0x55), BASE, ARG_NONE }, + { "setfpec2", FP(0x18,0x56), BASE, ARG_NONE }, + { "setfpec3", FP(0x18,0x57), BASE, ARG_NONE }, + { "fmas", FMA(0x19,0x00), BASE, ARG_FMA }, + { "fmad", FMA(0x19,0x01), BASE, ARG_FMA }, + { "fmss", FMA(0x19,0x02), BASE, ARG_FMA }, + { "fmsd", FMA(0x19,0x03), BASE, ARG_FMA }, + { "fnmas", FMA(0x19,0x04), BASE, ARG_FMA }, + { "fnmad", FMA(0x19,0x05), BASE, ARG_FMA }, + { "fnmss", FMA(0x19,0x06), BASE, ARG_FMA }, + { "fnmsd", FMA(0x19,0x07), BASE, ARG_FMA }, + { "fseleq", FMA(0x19,0x10), BASE, ARG_FMA }, + { "fselne", FMA(0x19,0x11), BASE, ARG_FMA }, + { "fsellt", FMA(0x19,0x12), BASE, ARG_FMA }, + { "fselle", FMA(0x19,0x13), BASE, ARG_FMA }, + { "fselgt", FMA(0x19,0x14), BASE, ARG_FMA }, + { "fselge", FMA(0x19,0x15), BASE, ARG_FMA }, + { "vaddw", FP(0x1A,0x00), BASE, ARG_FP }, + { "vaddw", FP(0x1A,0x20), BASE, ARG_FPL }, + { "vsubw", FP(0x1A,0x01), BASE, ARG_FP }, + { "vsubw", FP(0x1A,0x21), BASE, ARG_FPL }, + { "vcmpgew", FP(0x1A,0x02), BASE, ARG_FP }, + { "vcmpgew", FP(0x1A,0x22), BASE, ARG_FPL }, + { "vcmpeqw", FP(0x1A,0x03), BASE, ARG_FP }, + { "vcmpeqw", FP(0x1A,0x23), BASE, ARG_FPL }, + { "vcmplew", FP(0x1A,0x04), BASE, ARG_FP }, + { "vcmplew", FP(0x1A,0x24), BASE, ARG_FPL }, + { "vcmpltw", FP(0x1A,0x05), BASE, ARG_FP }, + { "vcmpltw", FP(0x1A,0x25), BASE, ARG_FPL }, + { "vcmpulew", FP(0x1A,0x06), BASE, ARG_FP }, + { "vcmpulew", FP(0x1A,0x26), BASE, ARG_FPL }, + { "vcmpultw", FP(0x1A,0x07), BASE, ARG_FP }, + { "vcmpultw", FP(0x1A,0x27), BASE, ARG_FPL }, + + { "vsllw", FP(0x1A,0x08), BASE, ARG_FP }, + { "vsllw", FP(0x1A,0x28), BASE, ARG_FPL }, + { "vsrlw", FP(0x1A,0x09), BASE, ARG_FP }, + { "vsrlw", FP(0x1A,0x29), BASE, ARG_FPL }, + { "vsraw", FP(0x1A,0x0A), BASE, ARG_FP }, + { "vsraw", FP(0x1A,0x2A), BASE, ARG_FPL }, + { "vrolw", FP(0x1A,0x0B), BASE, ARG_FP }, + { "vrolw", FP(0x1A,0x2B), BASE, ARG_FPL }, + { "sllow", FP(0x1A,0x0C), BASE, ARG_FP }, + { "sllow", FP(0x1A,0x2C), BASE, ARG_FPL }, + { "srlow", FP(0x1A,0x0D), BASE, ARG_FP }, + { "srlow", FP(0x1A,0x2D), BASE, ARG_FPL }, + { "vaddl", FP(0x1A,0x0E), BASE, ARG_FP }, + { "vaddl", FP(0x1A,0x2E), BASE, ARG_FPL }, + { "vsubl", FP(0x1A,0x0F), BASE, ARG_FP }, + { "vsubl", FP(0x1A,0x2F), BASE, ARG_FPL }, + { "ctpopow", FP(0x1A,0x18), BASE, { FA, ZB, DFC1 } }, + { "ctlzow", FP(0x1A,0x19), BASE, { FA, ZB, DFC1 } }, + { "vucaddw", FP(0x1A,0x40), BASE, ARG_FP }, + { "vucaddw", FP(0x1A,0x60), BASE, ARG_FPL }, + { "vucsubw", FP(0x1A,0x41), BASE, ARG_FP }, + { "vucsubw", FP(0x1A,0x61), BASE, ARG_FPL }, + { "vucaddh", FP(0x1A,0x42), BASE, ARG_FP }, + { "vucaddh", FP(0x1A,0x62), BASE, ARG_FPL }, + { "vucsubh", FP(0x1A,0x43), BASE, ARG_FP }, + { "vucsubh", FP(0x1A,0x63), BASE, ARG_FPL }, + { "vucaddb", FP(0x1A,0x44), BASE, ARG_FP }, + { "vucaddb", FP(0x1A,0x64), BASE, ARG_FPL }, + { "vucsubb", FP(0x1A,0x45), BASE, ARG_FP }, + { "vucsubb", FP(0x1A,0x65), BASE, ARG_FPL }, + { "vadds", FP(0x1A,0x80), BASE, ARG_FP }, + { "vaddd", FP(0x1A,0x81), BASE, ARG_FP }, + { "vsubs", FP(0x1A,0x82), BASE, ARG_FP }, + { "vsubd", FP(0x1A,0x83), BASE, ARG_FP }, + { "vmuls", FP(0x1A,0x84), BASE, ARG_FP }, + { "vmuld", FP(0x1A,0x85), BASE, ARG_FP }, + { "vdivs", FP(0x1A,0x86), BASE, ARG_FP }, + { "vdivd", FP(0x1A,0x87), BASE, ARG_FP }, + { "vsqrts", FP(0x1A,0x88), BASE, ARG_FPZ1 }, + { "vsqrtd", FP(0x1A,0x89), BASE, ARG_FPZ1 }, + { "vfcmpeq", FP(0x1A,0x8C), BASE, ARG_FP }, + { "vfcmple", FP(0x1A,0x8D), BASE, ARG_FP }, + { "vfcmplt", FP(0x1A,0x8E), BASE, ARG_FP }, + { "vfcmpun", FP(0x1A,0x8F), BASE, ARG_FP }, + { "vcpys", FP(0x1A,0x90), BASE, ARG_FP }, + { "vcpyse", FP(0x1A,0x91), BASE, ARG_FP }, + { "vcpysn", FP(0x1A,0x92), BASE, ARG_FP }, + { "vmas", FMA(0x1B,0x00), BASE, ARG_FMA }, + { "vmad", FMA(0x1B,0x01), BASE, ARG_FMA }, + { "vmss", FMA(0x1B,0x02), BASE, ARG_FMA }, + { "vmsd", FMA(0x1B,0x03), BASE, ARG_FMA }, + { "vnmas", FMA(0x1B,0x04), BASE, ARG_FMA }, + { "vnmad", FMA(0x1B,0x05), BASE, ARG_FMA }, + { "vnmss", FMA(0x1B,0x06), BASE, ARG_FMA }, + { "vnmsd", FMA(0x1B,0x07), BASE, ARG_FMA }, + { "vfseleq", FMA(0x1B,0x10), BASE, ARG_FMA }, + { "vfsellt", FMA(0x1B,0x12), BASE, ARG_FMA }, + { "vfselle", FMA(0x1B,0x13), BASE, ARG_FMA }, + { "vseleqw", FMA(0x1B,0x18), BASE, ARG_FMA }, + { "vseleqw", FMA(0x1B,0x38), BASE, ARG_FMAL }, + { "vsellbcw", FMA(0x1B,0x19), BASE, ARG_FMA }, + { "vsellbcw", FMA(0x1B,0x39), BASE, ARG_FMAL }, + { "vselltw", FMA(0x1B,0x1A), BASE, ARG_FMA }, + { "vselltw", FMA(0x1B,0x3A), BASE, ARG_FMAL }, + { "vsellew", FMA(0x1B,0x1B), BASE, ARG_FMA }, + { "vsellew", FMA(0x1B,0x3B), BASE, ARG_FMAL }, + { "vinsw", FMA(0x1B,0x20), BASE, ARG_FMAL }, + { "vinsf", FMA(0x1B,0x21), BASE, ARG_FMAL }, + { "vextw", FMA(0x1B,0x22), BASE, { FA, FMALIT, DFC1 }}, + { "vextf", FMA(0x1B,0x23), BASE, { FA, FMALIT, DFC1 }}, + { "vcpyw", FMA(0x1B,0x24), BASE, { FA, DFC1 }}, + { "vcpyf", FMA(0x1B,0x25), BASE, { FA, DFC1 }}, + { "vconw", FMA(0x1B,0x26), BASE, ARG_FMA }, + { "vshfw", FMA(0x1B,0x27), BASE, ARG_FMA }, + { "vcons", FMA(0x1B,0x28), BASE, ARG_FMA }, + { "vcond", FMA(0x1B,0x29), BASE, ARG_FMA }, + { "vldw_u", ATMEM(0x1C,0x0), BASE, ARG_VUAMEM }, + { "vstw_u", ATMEM(0x1C,0x1), BASE, ARG_VUAMEM }, + { "vlds_u", ATMEM(0x1C,0x2), BASE, ARG_VUAMEM }, + { "vsts_u", ATMEM(0x1C,0x3), BASE, ARG_VUAMEM }, + { "vldd_u", ATMEM(0x1C,0x4), BASE, ARG_VUAMEM }, + { "vstd_u", ATMEM(0x1C,0x5), BASE, ARG_VUAMEM }, + { "vstw_ul", ATMEM(0x1C,0x8), BASE, ARG_VUAMEM }, + { "vstw_uh", ATMEM(0x1C,0x9), BASE, ARG_VUAMEM }, + { "vsts_ul", ATMEM(0x1C,0xA), BASE, ARG_VUAMEM }, + { "vsts_uh", ATMEM(0x1C,0xB), BASE, ARG_VUAMEM }, + { "vstd_ul", ATMEM(0x1C,0xC), BASE, ARG_VUAMEM }, + { "vstd_uh", ATMEM(0x1C,0xD), BASE, ARG_VUAMEM }, + { "vldd_nc", ATMEM(0x1C,0xE), BASE, ARG_VUAMEM }, + { "vstd_nc", ATMEM(0x1C,0xF), BASE, ARG_VUAMEM }, + + { "flushd", MEM(0x20), BASE, ARG_PREFETCH }, + { "ldbu", MEM(0x20), BASE, ARG_MEM }, + { "evictdg", MEM(0x21), BASE, ARG_PREFETCH }, + { "ldhu", MEM(0x21), BASE, ARG_MEM }, + { "s_fillcs", MEM(0x22), BASE, ARG_PREFETCH }, + { "ldw", MEM(0x22), BASE, ARG_MEM }, + { "s_fillde", MEM(0x23), BASE, ARG_PREFETCH }, + { "ldl", MEM(0x23), BASE, ARG_MEM }, + { "evictdl", MEM(0x24), BASE, ARG_PREFETCH }, + { "ldl_u", MEM(0x24), BASE, ARG_MEM }, + { "pri_ldw/p", HWMEM(0x25,0x0), BASE, ARG_HWMEM }, + { "pri_ldw/v", HWMEM(0x25,0x8), BASE, ARG_HWMEM }, + { "pri_ldl/p", HWMEM(0x25,0x1), BASE, ARG_HWMEM }, + { "pri_ldl/v", HWMEM(0x25,0x9), BASE, ARG_HWMEM }, + { "fillde", MEM(0x26), BASE, ARG_PREFETCH }, + { "flds", MEM(0x26), BASE, ARG_FMEM }, + { "fillde_e", MEM(0x27), BASE, ARG_PREFETCH }, + { "fldd", MEM(0x27), BASE, ARG_FMEM }, + + { "stb", MEM(0x28), BASE, ARG_MEM }, + { "sth", MEM(0x29), BASE, ARG_MEM }, + { "stw", MEM(0x2A), BASE, ARG_MEM }, + { "stl", MEM(0x2B), BASE, ARG_MEM }, + { "stl_u", MEM(0x2C), BASE, ARG_MEM }, + { "pri_stw/p", HWMEM(0x2D,0x0), BASE, ARG_HWMEM }, + { "pri_stw/v", HWMEM(0x2D,0x8), BASE, ARG_HWMEM }, + { "pri_stl/p", HWMEM(0x2D,0x1), BASE, ARG_HWMEM }, + { "pri_stl/v", HWMEM(0x2D,0x9), BASE, ARG_HWMEM }, + { "fsts", MEM(0x2E), BASE, ARG_FMEM }, + { "fstd", MEM(0x2F), BASE, ARG_FMEM }, + { "beq", BRA(0x30), BASE, ARG_BRA }, + { "bne", BRA(0x31), BASE, ARG_BRA }, + { "blt", BRA(0x32), BASE, ARG_BRA }, + { "ble", BRA(0x33), BASE, ARG_BRA }, + { "bgt", BRA(0x34), BASE, ARG_BRA }, + { "bge", BRA(0x35), BASE, ARG_BRA }, + { "blbc", BRA(0x36), BASE, ARG_BRA }, + { "blbs", BRA(0x37), BASE, ARG_BRA }, + + { "fbeq", BRA(0x38), BASE, ARG_FBRA }, + { "fbne", BRA(0x39), BASE, ARG_FBRA }, + { "fblt", BRA(0x3A), BASE, ARG_FBRA }, + { "fble", BRA(0x3B), BASE, ARG_FBRA }, + { "fbgt", BRA(0x3C), BASE, ARG_FBRA }, + { "fbge", BRA(0x3D), BASE, ARG_FBRA }, + { "ldi", MEM(0x3E), BASE, ARG_MEM }, + { "ldih", MEM(0x3F), BASE, ARG_MEM }, +}; + +const unsigned sw_64_num_opcodes = sizeof(sw_64_opcodes) / sizeof(*sw_64_opcodes); + +/* OSF register names. */ + +static const char * const osf_regnames[64] = { + "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", + "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", + "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", + "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", + "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", + "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", + "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" +}; + +/* VMS register names. */ + +static const char * const vms_regnames[64] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23", + "R24", "AI", "RA", "PV", "AT", "FP", "SP", "RZ", + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", + "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", + "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", + "F24", "F25", "F26", "F27", "F28", "F29", "F30", "FZ" +}; + +int print_insn_sw_64(bfd_vma memaddr, struct disassemble_info *info) +{ + static const struct sw_64_opcode *opcode_index[SW_NOPS + 1]; + const char * const * regnames; + const struct sw_64_opcode *opcode, *opcode_end; + const unsigned char *opindex; + unsigned insn, op, isa_mask; + int need_comma; + + /* Initialize the majorop table the first time through */ + if (!opcode_index[0]) { + opcode = sw_64_opcodes; + opcode_end = opcode + sw_64_num_opcodes; + + for (op = 0; op < SW_NOPS; ++op) { + opcode_index[op] = opcode; + if ((SW_LITOP (opcode->opcode) != 0x10) && (SW_LITOP (opcode->opcode) != 0x11)) { + while (opcode < opcode_end && op == SW_OP (opcode->opcode)) + ++opcode; + } else { + while (opcode < opcode_end && op == SW_LITOP (opcode->opcode)) + ++opcode; + } + } + opcode_index[op] = opcode; + } + + if (info->flavour == bfd_target_evax_flavour) + regnames = vms_regnames; + else + regnames = osf_regnames; + isa_mask = SW_OPCODE_NOHM; + switch (info->mach) { + case bfd_mach_sw_64_core3: + isa_mask |= SW_OPCODE_BASE | SW_OPCODE_CORE3; + break; + } + + /* Read the insn into a host word */ + { + bfd_byte buffer[4]; + int status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status != 0) { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + insn = bfd_getl32 (buffer); + } + + /* Get the major opcode of the instruction. */ + if ((SW_LITOP (insn) == 0x10) || (SW_LITOP (insn) == 0x11)) + op = SW_LITOP (insn); + else if ((SW_OP(insn) & 0x3C) == 0x14 ) + op = 0x14; + else + op = SW_OP (insn); + + /* Find the first match in the opcode table. */ + opcode_end = opcode_index[op + 1]; + for (opcode = opcode_index[op]; opcode < opcode_end; ++opcode) { + if ((insn ^ opcode->opcode) & opcode->mask) + continue; + + if (!(opcode->flags & isa_mask)) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + { + int invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) { + const struct sw_64_operand *operand = sw_64_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + } + + /* The instruction is valid. */ + goto found; + } + + /* No instruction found */ + (*info->fprintf_func) (info->stream, ".long %#08x", insn); + + return 4; + +found: + if (!strncmp("sys_call",opcode->name,8)) { + if (insn & (0x1 << 25)) + (*info->fprintf_func) (info->stream, "%s", "sys_call"); + else + (*info->fprintf_func) (info->stream, "%s", "sys_call/b"); + } else + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + /* get zz[7:6] and zz[5:0] to form truth for vlog */ + if (!strcmp(opcode->name, "vlog")) + { + unsigned int truth; + char tr[4]; + truth=(SW_OP(insn) & 3) << 6; + truth = truth | ((insn & 0xFC00) >> 10); + sprintf(tr,"%x",truth); + (*info->fprintf_func) (info->stream, "%s", tr); + } + if (opcode->operands[0] != 0) + (*info->fprintf_func) (info->stream, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) { + const struct sw_64_operand *operand = sw_64_operands + *opindex; + int value; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & SW_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) NULL); + else { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if (operand->flags & SW_OPERAND_SIGNED) { + int signbit = 1 << (operand->bits - 1); + value = (value ^ signbit) - signbit; + } + } + + if (need_comma && + ((operand->flags & (SW_OPERAND_PARENS | SW_OPERAND_COMMA)) + != SW_OPERAND_PARENS)) { + (*info->fprintf_func) (info->stream, ","); + } + if (operand->flags & SW_OPERAND_PARENS) + (*info->fprintf_func) (info->stream, "("); + + /* Print the operand as directed by the flags. */ + if (operand->flags & SW_OPERAND_IR) + (*info->fprintf_func) (info->stream, "%s", regnames[value]); + else if (operand->flags & SW_OPERAND_FPR) + (*info->fprintf_func) (info->stream, "%s", regnames[value + 32]); + else if (operand->flags & SW_OPERAND_RELATIVE) + (*info->print_address_func) (memaddr + 4 + value, info); + else if (operand->flags & SW_OPERAND_SIGNED) + (*info->fprintf_func) (info->stream, "%d", value); + else + (*info->fprintf_func) (info->stream, "%#x", value); + + if (operand->flags & SW_OPERAND_PARENS) + (*info->fprintf_func) (info->stream, ")"); + need_comma = 1; + } + + return 4; +} diff --git a/hw/Kconfig b/hw/Kconfig index ad20cce0a9..5f3957be0f 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -63,6 +63,7 @@ source sparc/Kconfig source sparc64/Kconfig source tricore/Kconfig source xtensa/Kconfig +source sw64/Kconfig # Symbols used by multiple targets config TEST_DEVICES diff --git a/hw/meson.build b/hw/meson.build index b3366c888e..f39c1f7e70 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -62,5 +62,6 @@ subdir('s390x') subdir('sh4') subdir('sparc') subdir('sparc64') +subdir('sw64') subdir('tricore') subdir('xtensa') diff --git a/hw/rtc/sun4v-rtc.c b/hw/rtc/sun4v-rtc.c index e037acd1b5..58a0cff483 100644 --- a/hw/rtc/sun4v-rtc.c +++ b/hw/rtc/sun4v-rtc.c @@ -32,10 +32,17 @@ static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr, unsigned size) { uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND; +#if defined(__sw_64__) + if (addr & 4ULL) { + /* accessing the high 32 bits */ + val >>= 32; + } +#else if (!(addr & 4ULL)) { /* accessing the high 32 bits */ val >>= 32; } +#endif trace_sun4v_rtc_read(addr, val); return val; } @@ -49,7 +56,11 @@ static void sun4v_rtc_write(void *opaque, hwaddr addr, static const MemoryRegionOps sun4v_rtc_ops = { .read = sun4v_rtc_read, .write = sun4v_rtc_write, +#if defined(__sw_64__) + .endianness = DEVICE_LITTLE_ENDIAN, +#else .endianness = DEVICE_NATIVE_ENDIAN, +#endif }; void sun4v_rtc_init(hwaddr addr) diff --git a/hw/sw64/Kconfig b/hw/sw64/Kconfig new file mode 100644 index 0000000000..2bf19e8234 --- /dev/null +++ b/hw/sw64/Kconfig @@ -0,0 +1,11 @@ +config CORE3 + bool + imply PCI_DEVICES + imply TEST_DEVICES + imply E1000_PCI + select PCI_EXPRESS + select SUN4V_RTC + select VIRTIO_MMIO + select SERIAL + select IDE_CMD646 + select VIRTIO_VGA diff --git a/hw/sw64/Makefile.objs b/hw/sw64/Makefile.objs new file mode 100644 index 0000000000..73add9a91d --- /dev/null +++ b/hw/sw64/Makefile.objs @@ -0,0 +1 @@ +obj-y += core3.o core3_board.o diff --git a/hw/sw64/core.h b/hw/sw64/core.h new file mode 100644 index 0000000000..4923382229 --- /dev/null +++ b/hw/sw64/core.h @@ -0,0 +1,25 @@ +#ifndef HW_SW64_SYS_H +#define HW_SW64_SYS_H + +typedef struct boot_params { + unsigned long initrd_size; /* size of initrd */ + unsigned long initrd_start; /* logical address of initrd */ + unsigned long dtb_start; /* logical address of dtb */ + unsigned long efi_systab; /* logical address of EFI system table */ + unsigned long efi_memmap; /* logical address of EFI memory map */ + unsigned long efi_memmap_size; /* size of EFI memory map */ + unsigned long efi_memdesc_size; /* size of an EFI memory map descriptor */ + unsigned long efi_memdesc_version; /* memory descriptor version */ + unsigned long cmdline; /* logical address of cmdline */ +} BOOT_PARAMS; + +void core3_board_init(SW64CPU *cpus[4], MemoryRegion *ram); +#endif + +#define MAX_CPUS 64 + +#ifdef CONFIG_KVM +#define MAX_CPUS_CORE3 64 +#else +#define MAX_CPUS_CORE3 32 +#endif diff --git a/hw/sw64/core3.c b/hw/sw64/core3.c new file mode 100644 index 0000000000..dbe4ed6fa1 --- /dev/null +++ b/hw/sw64/core3.c @@ -0,0 +1,182 @@ +/* + * QEMU CORE3 hardware system emulator. + * + * Copyright (c) 2021 Lu Feifei + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/datadir.h" +#include "cpu.h" +#include "hw/hw.h" +#include "elf.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "qemu/error-report.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/reset.h" +#include "hw/ide.h" +#include "hw/char/serial.h" +#include "qemu/cutils.h" +#include "ui/console.h" +#include "core.h" +#include "hw/boards.h" +#include "sysemu/numa.h" + +static uint64_t cpu_sw64_virt_to_phys(void *opaque, uint64_t addr) +{ + return addr &= ~0xffffffff80000000 ; +} + +static CpuInstanceProperties +sw64_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + + assert(cpu_index < possible_cpus->len); + return possible_cpus->cpus[cpu_index].props; +} + +static int64_t sw64_get_default_cpu_node_id(const MachineState *ms, int idx) +{ + int nb_numa_nodes = ms->numa_state->num_nodes; + return idx % nb_numa_nodes; +} + +static const CPUArchIdList *sw64_possible_cpu_arch_ids(MachineState *ms) +{ + int i; + unsigned int max_cpus = ms->smp.max_cpus; + + if (ms->possible_cpus) { + /* + * make sure that max_cpus hasn't changed since the first use, i.e. + * -smp hasn't been parsed after it + */ + assert(ms->possible_cpus->len == max_cpus); + return ms->possible_cpus; + } + + ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + + sizeof(CPUArchId) * max_cpus); + ms->possible_cpus->len = max_cpus; + for (i = 0; i < ms->possible_cpus->len; i++) { + ms->possible_cpus->cpus[i].type = ms->cpu_type; + ms->possible_cpus->cpus[i].vcpus_count = 1; + ms->possible_cpus->cpus[i].arch_id = i; + ms->possible_cpus->cpus[i].props.has_thread_id = true; + ms->possible_cpus->cpus[i].props.core_id = i; + } + + return ms->possible_cpus; +} + +static void core3_cpu_reset(void *opaque) +{ + SW64CPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +static void core3_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + ram_addr_t buf; + SW64CPU *cpus[machine->smp.max_cpus]; + long i, size; + const char *kernel_filename = machine->kernel_filename; + const char *kernel_cmdline = machine->kernel_cmdline; + char *hmcode_filename; + char *uefi_filename; + uint64_t hmcode_entry, hmcode_low, hmcode_high; + uint64_t kernel_entry, kernel_low, kernel_high; + BOOT_PARAMS *core3_boot_params = g_new0(BOOT_PARAMS, 1); + uint64_t param_offset; + + memset(cpus, 0, sizeof(cpus)); + + for (i = 0; i < machine->smp.cpus; ++i) { + cpus[i] = SW64_CPU(cpu_create(machine->cpu_type)); + cpus[i]->env.csr[CID] = i; + qemu_register_reset(core3_cpu_reset, cpus[i]); + } + core3_board_init(cpus, machine->ram); + if (kvm_enabled()) + buf = ram_size; + else + buf = ram_size | (1UL << 63); + + rom_add_blob_fixed("ram_size", (char *)&buf, 0x8, 0x2040); + + param_offset = 0x90B000UL; + core3_boot_params->cmdline = param_offset | 0xfff0000000000000UL; + rom_add_blob_fixed("core3_boot_params", (core3_boot_params), 0x48, 0x90A100); + + hmcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, kvm_enabled() ? "core3-reset":"core3-hmcode"); + if (hmcode_filename == NULL) { + if (kvm_enabled()) + error_report("no core3-reset provided"); + else + error_report("no core3-hmcode provided"); + exit(1); + } + size = load_elf(hmcode_filename, NULL, cpu_sw64_virt_to_phys, NULL, + &hmcode_entry, &hmcode_low, &hmcode_high, NULL, 0, EM_SW64, 0, 0); + if (size < 0) { + if (kvm_enabled()) + error_report("could not load core3-reset: '%s'", hmcode_filename); + else + error_report("could not load core3-hmcode: '%s'", hmcode_filename); + exit(1); + } + g_free(hmcode_filename); + + /* Start all cpus at the hmcode RESET entry point. */ + for (i = 0; i < machine->smp.cpus; ++i) { + cpus[i]->env.pc = hmcode_entry; + cpus[i]->env.hm_entry = hmcode_entry; + } + + if (!kernel_filename) { + uefi_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "uefi-bios-sw"); + load_image_targphys(uefi_filename, 0x2f00000UL, -1); + g_free(uefi_filename); + } else { + /* Load a kernel. */ + size = load_elf(kernel_filename, NULL, cpu_sw64_virt_to_phys, NULL, + &kernel_entry, &kernel_low, &kernel_high, NULL, 0, EM_SW64, 0, 0); + if (size < 0) { + error_report("could not load kernel '%s'", kernel_filename); + exit(1); + } + cpus[0]->env.trap_arg1 = kernel_entry; + if (kernel_cmdline) + pstrcpy_targphys("cmdline", param_offset, 0x400, kernel_cmdline); + } +} + +static void board_reset(MachineState *state) +{ + qemu_devices_reset(); +} + +static void core3_machine_init(MachineClass *mc) +{ + mc->desc = "core3 BOARD"; + mc->init = core3_init; + mc->block_default_type = IF_IDE; + mc->max_cpus = MAX_CPUS_CORE3; + mc->is_default = 0; + mc->reset = board_reset; + mc->possible_cpu_arch_ids = sw64_possible_cpu_arch_ids; + mc->cpu_index_to_instance_props = sw64_cpu_index_to_props; + mc->default_cpu_type = SW64_CPU_TYPE_NAME("core3"); + mc->default_ram_id = "ram"; + mc->get_default_cpu_node_id = sw64_get_default_cpu_node_id; +} + +DEFINE_MACHINE("core3", core3_machine_init) diff --git a/hw/sw64/core3_board.c b/hw/sw64/core3_board.c new file mode 100644 index 0000000000..7853e01edb --- /dev/null +++ b/hw/sw64/core3_board.c @@ -0,0 +1,493 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "core.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci.h" +#include "hw/char/serial.h" +#include "hw/irq.h" +#include "net/net.h" +#include "hw/usb.h" +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" +#include "sysemu/numa.h" +#include "sysemu/kvm.h" +#include "hw/rtc/sun4v-rtc.h" +#include "hw/pci/msi.h" +#include "hw/sw64/sw64_iommu.h" + +#define TYPE_SWBOARD_PCI_HOST_BRIDGE "core_board-pcihost" +#define SWBOARD_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(BoardState, (obj), TYPE_SWBOARD_PCI_HOST_BRIDGE) + +#define MAX_IDE_BUS 2 +#define SW_PIN_TO_IRQ 16 + +typedef struct SWBoard { + SW64CPU *cpu[MAX_CPUS_CORE3]; +} SWBoard; + +typedef struct BoardState { + PCIHostState parent_obj; + + SWBoard sboard; + uint64_t expire_time; +} BoardState; + +typedef struct TimerState { + void *opaque; + int order; +} TimerState; + +#ifndef CONFIG_KVM +static void swboard_alarm_timer(void *opaque) +{ + TimerState *ts = (TimerState *)((uintptr_t)opaque); + BoardState *bs = (BoardState *)((uintptr_t)ts->opaque); + + int cpu = ts->order; + cpu_interrupt(CPU(bs->sboard.cpu[cpu]), CPU_INTERRUPT_TIMER); +} +#endif + +static PCIINTxRoute sw_route_intx_pin_to_irq(void *opaque, int pin) +{ + PCIINTxRoute route; + + route.mode = PCI_INTX_ENABLED; + route.irq = SW_PIN_TO_IRQ; + return route; +} + +static uint64_t convert_bit(int n) +{ + uint64_t ret = (1UL << n) - 1; + + if (n == 64) + ret = 0xffffffffffffffffUL; + return ret; +} + +static uint64_t mcu_read(void *opaque, hwaddr addr, unsigned size) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int smp_cpus = ms->smp.cpus; + uint64_t ret = 0; + switch (addr) { + case 0x0000: + /* CG_ONLINE */ + { + int i; + for (i = 0; i < smp_cpus; i = i + 4) + ret |= (1UL << i); + } + break; + /*IO_START*/ + case 0x1300: + ret = 0x1; + break; + case 0x3780: + /* MC_ONLINE */ + ret = convert_bit(smp_cpus); + break; + case 0x0900: + /* CPUID */ + ret = 0; + break; + case 0x1180: + /* LONGTIME */ + ret = qemu_clock_get_ns(QEMU_CLOCK_HOST) / 80; + break; + case 0x4900: + /* MC_CONFIG */ + break; + case 0x0780: + /* CORE_ONLINE */ + ret = convert_bit(smp_cpus); + break; + case 0x0680: + /* INIT_CTL */ + ret = 0x000003AE00000D28; + break; + default: + fprintf(stderr, "Unsupported MCU addr: 0x%04lx\n", addr); + return -1; + } + return ret; +} + +static void mcu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ +#ifndef CONFIG_KVM +#ifdef CONFIG_DUMP_PRINTK + uint64_t print_addr; + uint32_t len; + int i; + + if (addr == 0x40000) { + print_addr = val & 0x7fffffff; + len = (uint32_t)(val >> 32); + uint8_t *buf; + buf = malloc(len + 10); + memset(buf, 0, len + 10); + cpu_physical_memory_rw(print_addr, buf, len, 0); + for (i = 0; i < len; i++) + printf("%c", buf[i]); + + free(buf); + return; + } +#endif +#endif +} + +static const MemoryRegionOps mcu_ops = { + .read = mcu_read, + .write = mcu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = + { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = + { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t intpu_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t ret = 0; +#ifndef CONFIG_KVM + switch (addr) { + case 0x180: + /* LONGTIME */ + ret = qemu_clock_get_ns(QEMU_CLOCK_HOST) / 32; + break; + } +#endif + return ret; +} + +static void intpu_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ +#ifndef CONFIG_KVM + BoardState *bs = (BoardState *)opaque; + SW64CPU *cpu; + switch (addr) { + case 0x00: + val &= 0x1f; + cpu = bs->sboard.cpu[val]; + cpu->env.csr[II_REQ] = 0x100000; + cpu_interrupt(CPU(cpu),CPU_INTERRUPT_IIMAIL); + break; + default: + fprintf(stderr, "Unsupported IPU addr: 0x%04lx\n", addr); + break; + } +#endif +} + +static const MemoryRegionOps intpu_ops = { + .read = intpu_read, + .write = intpu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = + { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = + { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static MemTxResult msi_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + return MEMTX_OK; +} + +MemTxResult msi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ +#ifdef CONFIG_KVM + int ret = 0; + MSIMessage msg = {}; + + msg.address = (uint64_t) addr + 0x8000fee00000; + msg.data = (uint32_t) value; + + ret = kvm_irqchip_send_msi(kvm_state, msg); + if (ret < 0) { + fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n", + strerror(-ret)); + } +#endif + return MEMTX_OK; +} + +static const MemoryRegionOps msi_ops = { + .read_with_attrs = msi_read, + .write_with_attrs = msi_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = + { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = + { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static uint64_t ignore_read(void *opaque, hwaddr addr, unsigned size) +{ + return 1; +} + +static void ignore_write(void *opaque, hwaddr addr, uint64_t v, unsigned size) +{ +} + +const MemoryRegionOps core3_pci_ignore_ops = { + .read = ignore_read, + .write = ignore_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = + { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = + { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static uint64_t config_read(void *opaque, hwaddr addr, unsigned size) +{ + PCIBus *b = opaque; + uint32_t trans_addr = 0; + trans_addr |= ((addr >> 16) & 0xffff) << 8; + trans_addr |= (addr & 0xff); + return pci_data_read(b, trans_addr, size); +} + +static void config_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PCIBus *b = opaque; + uint32_t trans_addr = 0; + trans_addr |= ((addr >> 16) & 0xffff) << 8; + trans_addr |= (addr & 0xff); + pci_data_write(b, trans_addr, val, size); +} + +const MemoryRegionOps core3_pci_config_ops = { + .read = config_read, + .write = config_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = + { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = + { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static void cpu_irq_change(SW64CPU *cpu, uint64_t req) +{ + if (cpu != NULL) { + CPUState *cs = CPU(cpu); + if (req) + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +static void swboard_set_irq(void *opaque, int irq, int level) +{ + BoardState *bs = opaque; + SW64CPU *cpu; + int i; + + if (kvm_enabled()) { + if (level == 0) + return; + kvm_set_irq(kvm_state, irq, level); + return; + } + + for (i = 0; i < 1; i++) { + cpu = bs->sboard.cpu[i]; + if (cpu != NULL) { + CPUState *cs = CPU(cpu); + if (level) + cpu_interrupt(cs, CPU_INTERRUPT_PCIE); + else + cpu_reset_interrupt(cs, CPU_INTERRUPT_PCIE); + } + } +} + +static int swboard_map_irq(PCIDevice *d, int irq_num) +{ + /* In fact,the return value is the interrupt type passed to kernel, + * so it must keep same with the type in do_entInt in kernel. + */ + return 16; +} + +static void serial_set_irq(void *opaque, int irq, int level) +{ + BoardState *bs = (BoardState *)opaque; + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int smp_cpus = ms->smp.cpus; + int i; + if (level == 0) + return; + if (kvm_enabled()) { + kvm_set_irq(kvm_state, irq, level); + return; + } + for (i = 0; i < smp_cpus; i++) { + if (bs->sboard.cpu[i]) + cpu_irq_change(bs->sboard.cpu[i], 1); + } +} + +void core3_board_init(SW64CPU *cpus[MAX_CPUS], MemoryRegion *ram) +{ + DeviceState *dev; + BoardState *bs; +#ifndef CONFIG_KVM + TimerState *ts; +#endif + MemoryRegion *io_mcu = g_new(MemoryRegion, 1); + MemoryRegion *io_intpu = g_new(MemoryRegion, 1); + MemoryRegion *msi_ep = g_new(MemoryRegion, 1); + qemu_irq serial_irq; + uint64_t MB = 1024 * 1024; + MemoryRegion *mem_ep = g_new(MemoryRegion, 1); + MemoryRegion *mem_ep64 = g_new(MemoryRegion, 1); + MemoryRegion *conf_piu0 = g_new(MemoryRegion, 1); + MemoryRegion *io_ep = g_new(MemoryRegion, 1); + + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int smp_cpus = ms->smp.cpus; + + PCIBus *b; + PCIHostState *phb; + uint64_t GB = 1024 * MB; + + int i; + dev = qdev_new(TYPE_SWBOARD_PCI_HOST_BRIDGE); + phb = PCI_HOST_BRIDGE(dev); + bs = SWBOARD_PCI_HOST_BRIDGE(dev); + +#ifdef CONFIG_KVM + if (kvm_has_gsi_routing()) + msi_nonbroken = true; +#endif + + for (i = 0; i < smp_cpus; ++i) { + if (cpus[i] == NULL) + continue; + bs->sboard.cpu[i] = cpus[i]; +#ifndef CONFIG_KVM + ts = g_new(TimerState, 1); + ts->opaque = (void *) ((uintptr_t)bs); + ts->order = i; + cpus[i]->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &swboard_alarm_timer, ts); +#endif + } + memory_region_add_subregion(get_system_memory(), 0, ram); + + memory_region_init_io(io_mcu, NULL, &mcu_ops, bs, "io_mcu", 16 * MB); + memory_region_add_subregion(get_system_memory(), 0x803000000000ULL, io_mcu); + + memory_region_init_io(io_intpu, NULL, &intpu_ops, bs, "io_intpu", 1 * MB); + memory_region_add_subregion(get_system_memory(), 0x802a00000000ULL, + io_intpu); + + memory_region_init_io(msi_ep, NULL, &msi_ops, bs, "msi_ep", 1 * MB); + memory_region_add_subregion(get_system_memory(), 0x8000fee00000ULL, msi_ep); + + memory_region_init(mem_ep, OBJECT(bs), "pci0-mem", 0x890000000000ULL); + memory_region_add_subregion(get_system_memory(), 0x880000000000ULL, mem_ep); + + memory_region_init_alias(mem_ep64, NULL, "mem_ep64", mem_ep, 0x888000000000ULL, 1ULL << 39); + memory_region_add_subregion(get_system_memory(), 0x888000000000ULL, mem_ep64); + + memory_region_init_io(io_ep, OBJECT(bs), &core3_pci_ignore_ops, NULL, + "pci0-io-ep", 4 * GB); + + memory_region_add_subregion(get_system_memory(), 0x880100000000ULL, io_ep); + b = pci_register_root_bus(dev, "pcie.0", swboard_set_irq, swboard_map_irq, bs, + mem_ep, io_ep, 0, 537, TYPE_PCIE_BUS); + phb->bus = b; + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + pci_bus_set_route_irq_fn(b, sw_route_intx_pin_to_irq); + memory_region_init_io(conf_piu0, OBJECT(bs), &core3_pci_config_ops, b, + "pci0-ep-conf-io", 4 * GB); + memory_region_add_subregion(get_system_memory(), 0x880600000000ULL, + conf_piu0); +#ifdef SW64_VT_IOMMU + sw64_vt_iommu_init(b); +#endif + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], b, "e1000", NULL); + } + + pci_vga_init(b); +#define MAX_SATA_PORTS 6 + PCIDevice *ahci; + DriveInfo *hd[MAX_SATA_PORTS]; + ahci = pci_create_simple_multifunction(b, PCI_DEVFN(0x1f, 0), true, + TYPE_ICH9_AHCI); + g_assert(MAX_SATA_PORTS == ahci_get_num_ports(ahci)); + ide_drive_get(hd, ahci_get_num_ports(ahci)); + ahci_ide_create_devs(ahci, hd); + + serial_irq = qemu_allocate_irq(serial_set_irq, bs, 12); + if (serial_hd(0)) { + serial_mm_init(get_system_memory(), 0x3F8 + 0x880100000000ULL, 0, + serial_irq, (1843200 >> 4), serial_hd(0), + DEVICE_LITTLE_ENDIAN); + } + pci_create_simple(phb->bus, -1, "nec-usb-xhci"); + sun4v_rtc_init(0x804910000000ULL); +} + +static const TypeInfo swboard_pcihost_info = { + .name = TYPE_SWBOARD_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(BoardState), +}; + +static void swboard_register_types(void) +{ + type_register_static(&swboard_pcihost_info); +} + +type_init(swboard_register_types) diff --git a/hw/sw64/meson.build b/hw/sw64/meson.build new file mode 100644 index 0000000000..8abb18222a --- /dev/null +++ b/hw/sw64/meson.build @@ -0,0 +1,10 @@ +sw64_ss = ss.source_set() + +sw64_ss.add(files('sw64_iommu.c')) + +sw64_ss.add(when: 'CONFIG_CORE3', if_true: files( + 'core3.c', + 'core3_board.c', +)) + +hw_arch += {'sw64': sw64_ss} diff --git a/hw/sw64/sw64_iommu.c b/hw/sw64/sw64_iommu.c new file mode 100644 index 0000000000..8ded65f213 --- /dev/null +++ b/hw/sw64/sw64_iommu.c @@ -0,0 +1,567 @@ +/* + * QEMU sw64 IOMMU emulation + * + * Copyright (c) 2021 Lu Feifei + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/sw64/sw64_iommu.h" +#include "sysemu/kvm.h" + +#define IOMMU_PAGE_SHIFT 13 +#define IOMMU_PAGE_SIZE_8K (1ULL << IOMMU_PAGE_SHIFT) +#define IOMMU_PAGE_MASK_8K (~(IOMMU_PAGE_SIZE_8K - 1)) +#define IOMMU_IOVA_SHIFT 16 +#define SW64IOMMU_PTIOTLB_MAX_SIZE 256 + +static MemTxResult swvt_msi_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, MemTxAttrs attrs) +{ + return MEMTX_OK; +} + +static MemTxResult swvt_msi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + MemTxResult ret; + + ret = msi_write(opaque, addr, value, size, attrs); + + return ret; +} + +static const MemoryRegionOps swvt_msi_ops = { + .read_with_attrs = swvt_msi_read, + .write_with_attrs = swvt_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +SWVTAddressSpace *iommu_find_add_as(SW64IOMMUState *s, PCIBus *bus, int devfn) +{ + uintptr_t key = (uintptr_t)bus; + SWVTBus *swvt_bus = g_hash_table_lookup(s->swvtbus_as_by_busptr, &key); + SWVTAddressSpace *swvt_dev_as; + char name[128]; + + if (!swvt_bus) { + uintptr_t *new_key = g_malloc(sizeof(*new_key)); + *new_key = (uintptr_t)bus; + /* No corresponding free() */ + swvt_bus = g_malloc0(sizeof(SWVTBus) + sizeof(SWVTAddressSpace *) * \ + PCI_DEVFN_MAX); + swvt_bus->bus = bus; + g_hash_table_insert(s->swvtbus_as_by_busptr, new_key, swvt_bus); + } + swvt_dev_as = swvt_bus->dev_as[devfn]; + if (!swvt_dev_as) { + snprintf(name, sizeof(name), "sw64_iommu_devfn_%d", devfn); + swvt_bus->dev_as[devfn] = swvt_dev_as = g_malloc0(sizeof(SWVTAddressSpace)); + + swvt_dev_as->bus = bus; + swvt_dev_as->devfn = (uint8_t)devfn; + swvt_dev_as->iommu_state = s; + + memory_region_init_iommu(&swvt_dev_as->iommu, sizeof(swvt_dev_as->iommu), + TYPE_SW64_IOMMU_MEMORY_REGION, OBJECT(s), + "sw64_iommu_dmar", + 1UL << 32); + memory_region_init_io(&swvt_dev_as->msi, OBJECT(s), + &swvt_msi_ops, s, "sw_msi", 1 * 1024 * 1024); + memory_region_init(&swvt_dev_as->root, OBJECT(s), + "swvt_root", UINT64_MAX); + memory_region_add_subregion_overlap(&swvt_dev_as->root, + 0x8000fee00000ULL, + &swvt_dev_as->msi, 64); + address_space_init(&swvt_dev_as->as, &swvt_dev_as->root, name); + memory_region_add_subregion_overlap(&swvt_dev_as->root, 0, + MEMORY_REGION(&swvt_dev_as->iommu), + 1); + } + + memory_region_set_enabled(MEMORY_REGION(&swvt_dev_as->iommu), true); + + return swvt_dev_as; +} + +/** + * get_pte - Get the content of a page table entry located at + * @base_addr[@index] + */ +static int get_pte(dma_addr_t baseaddr, uint64_t *pte) +{ + int ret; + + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, baseaddr, + (uint8_t *)pte, sizeof(*pte)); + + if (ret != MEMTX_OK) + return -EINVAL; + + return 0; +} + +static bool swvt_do_iommu_translate(SWVTAddressSpace *swvt_as, PCIBus *bus, + uint8_t devfn, hwaddr addr, IOMMUTLBEntry *entry) +{ + SW64IOMMUState *s = swvt_as->iommu_state; + uint8_t bus_num = pci_bus_num(bus); + unsigned long dtbbaseaddr, dtbbasecond; + unsigned long pdebaseaddr, ptebaseaddr; + unsigned long pte; + uint16_t source_id; + SW64DTIOTLBEntry *dtcached_entry = NULL; + SW64DTIOTLBKey dtkey, *new_key; + + dtcached_entry = g_hash_table_lookup(s->dtiotlb, &dtkey); + + if (unlikely(!dtcached_entry)) { + dtbbaseaddr = s->dtbr + (bus_num << 3); + + if (get_pte(dtbbaseaddr, &pte)) + goto error; + + dtbbasecond = (pte & (~(SW_IOMMU_ENTRY_VALID))) + (devfn << 3); + if (get_pte(dtbbasecond, &pte)) + goto error; + + source_id = ((bus_num & 0xffUL) << 8) | (devfn & 0xffUL); + dtcached_entry = g_new0(SW64DTIOTLBEntry, 1); + dtcached_entry->ptbase_addr = pte & (~(SW_IOMMU_ENTRY_VALID)); + dtcached_entry->source_id = source_id; + + new_key = g_new0(SW64DTIOTLBKey, 1); + new_key->source_id = source_id; + + g_hash_table_insert(s->dtiotlb, new_key, dtcached_entry); + } + + pdebaseaddr = dtcached_entry->ptbase_addr; + pdebaseaddr += ((addr >> 23) & SW_IOMMU_LEVEL1_OFFSET) << 3; + + if (get_pte(pdebaseaddr, &pte)) + goto error; + + ptebaseaddr = pte & (~(SW_IOMMU_ENTRY_VALID)); + ptebaseaddr += ((addr >> IOMMU_PAGE_SHIFT) & SW_IOMMU_LEVEL2_OFFSET) << 3; + + if (get_pte(ptebaseaddr, &pte)) + goto error; + + pte &= ~(SW_IOMMU_ENTRY_VALID | SW_IOMMU_GRN | SW_IOMMU_ENABLE); + entry->translated_addr = pte; + entry->addr_mask = IOMMU_PAGE_SIZE_8K - 1; + + return 0; + +error: + entry->perm = IOMMU_NONE; + return -EINVAL; +} + +static void swvt_ptiotlb_inv_all(SW64IOMMUState *s) +{ + g_hash_table_remove_all(s->ptiotlb); +} + +static void swvt_lookup_ptiotlb(SW64IOMMUState *s, uint16_t source_id, + hwaddr addr, IOMMUTLBEntry *entry) +{ + SW64PTIOTLBKey ptkey; + + ptkey.source_id = source_id; + ptkey.iova = addr; + + entry = g_hash_table_lookup(s->ptiotlb, &ptkey); +} + +static IOMMUTLBEntry sw64_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, + IOMMUAccessFlags flag, int iommu_idx) +{ + SWVTAddressSpace *swvt_as = container_of(iommu, SWVTAddressSpace, iommu); + SW64IOMMUState *s = swvt_as->iommu_state; + IOMMUTLBEntry *cached_entry = NULL; + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = addr, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + uint8_t bus_num = pci_bus_num(swvt_as->bus); + uint16_t source_id; + SW64PTIOTLBKey *new_ptkey; + hwaddr aligned_addr; + + source_id = ((bus_num & 0xffUL) << 8) | (swvt_as->devfn & 0xffUL); + + qemu_mutex_lock(&s->iommu_lock); + + aligned_addr = addr & IOMMU_PAGE_MASK_8K; + + swvt_lookup_ptiotlb(s, aligned_addr, source_id, cached_entry); + + if (cached_entry) + goto out; + + if (g_hash_table_size(s->ptiotlb) >= SW64IOMMU_PTIOTLB_MAX_SIZE) { + swvt_ptiotlb_inv_all(s); + } + + cached_entry = g_new0(IOMMUTLBEntry, 1); + + if (swvt_do_iommu_translate(swvt_as, swvt_as->bus, swvt_as->devfn, + addr, cached_entry)) { + g_free(cached_entry); + qemu_mutex_unlock(&s->iommu_lock); + printf("%s: detected translation failure " + "(busnum=%d, devfn=%#x, iova=%#lx.\n", + __func__, pci_bus_num(swvt_as->bus), swvt_as->devfn, + entry.iova); + entry.iova = 0; + entry.translated_addr = 0; + entry.addr_mask = 0; + entry.perm = IOMMU_NONE; + + return entry; + } else { + new_ptkey = g_new0(SW64PTIOTLBKey, 1); + new_ptkey->source_id = source_id; + new_ptkey->iova = aligned_addr; + g_hash_table_insert(s->ptiotlb, new_ptkey, cached_entry); + } + +out: + qemu_mutex_unlock(&s->iommu_lock); + entry.perm = flag; + entry.translated_addr = cached_entry->translated_addr + + (addr & (IOMMU_PAGE_SIZE_8K - 1)); + entry.addr_mask = cached_entry->addr_mask; + + return entry; +} + +static void swvt_ptiotlb_inv_iova(SW64IOMMUState *s, uint16_t source_id, dma_addr_t iova) +{ + SW64PTIOTLBKey key = {.source_id = source_id, .iova = iova}; + + qemu_mutex_lock(&s->iommu_lock); + g_hash_table_remove(s->ptiotlb, &key); + qemu_mutex_unlock(&s->iommu_lock); +} + +void swvt_address_space_unmap_iova(SW64IOMMUState *s, unsigned long val) +{ + SWVTAddressSpace *swvt_as; + IOMMUNotifier *n; + uint16_t source_id; + dma_addr_t iova; + IOMMUTLBEvent event; + + source_id = val & 0xffff; + iova = (val >> IOMMU_IOVA_SHIFT) << IOMMU_PAGE_SHIFT; + + swvt_ptiotlb_inv_iova(s, source_id, iova); + + QLIST_FOREACH(swvt_as, &s->swvt_as_with_notifiers, next) { + uint8_t bus_num = pci_bus_num(swvt_as->bus); + uint16_t as_sourceid = ((bus_num & 0xffUL) << 8) | (swvt_as->devfn & 0xffUL); + + if (as_sourceid == source_id) { + IOMMU_NOTIFIER_FOREACH(n, &swvt_as->iommu) { + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.iova = iova & IOMMU_PAGE_MASK_8K; + event.entry.translated_addr = 0; + event.entry.perm = IOMMU_NONE; + event.entry.addr_mask = IOMMU_PAGE_SIZE_8K - 1; + + memory_region_notify_iommu(&swvt_as->iommu, 0, event); + } + } + } +} + +/* Unmap the whole range in the notifier's scope. */ +static void swvt_address_space_unmap(SWVTAddressSpace *as, IOMMUNotifier *n) +{ + IOMMUTLBEvent event; + hwaddr size; + hwaddr start = n->start; + hwaddr end = n->end; + + assert(start <= end); + size = end - start; + + event.entry.target_as = &address_space_memory; + /* Adjust iova for the size */ + event.entry.iova = n->start & ~(size - 1); + /* This field is meaningless for unmap */ + event.entry.translated_addr = 0; + event.entry.perm = IOMMU_NONE; + event.entry.addr_mask = size - 1; + + memory_region_notify_iommu_one(n, &event); +} + +void swvt_address_space_map_iova(SW64IOMMUState *s, unsigned long val) +{ + SWVTAddressSpace *swvt_as; + IOMMUNotifier *n; + uint16_t source_id; + dma_addr_t iova; + IOMMUTLBEvent event; + int ret; + + source_id = val & 0xffff; + iova = (val >> IOMMU_IOVA_SHIFT) << IOMMU_PAGE_SHIFT; + + swvt_ptiotlb_inv_iova(s, source_id, iova); + + QLIST_FOREACH(swvt_as, &s->swvt_as_with_notifiers, next) { + uint8_t bus_num = pci_bus_num(swvt_as->bus); + uint16_t as_sourceid = ((bus_num & 0xffUL) << 8) | (swvt_as->devfn & 0xffUL); + + if (as_sourceid == source_id) { + IOMMU_NOTIFIER_FOREACH(n, &swvt_as->iommu) { + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.iova = iova & IOMMU_PAGE_MASK_8K; + event.entry.perm = IOMMU_RW; + + ret = swvt_do_iommu_translate(swvt_as, swvt_as->bus, + swvt_as->devfn, iova, &event.entry); + if (ret) + goto out; + + memory_region_notify_iommu(&swvt_as->iommu, 0, event); + } + } + } +out: + return; +} + +void swvt_address_space_invalidate_iova(SW64IOMMUState *s, unsigned long val) +{ + int map_flag; + + map_flag = val >> 36; + + if (map_flag) + swvt_address_space_map_iova(s, val & 0xfffffffff); + else + swvt_address_space_unmap_iova(s, val); + + return; +} + +static AddressSpace *sw64_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + SW64IOMMUState *s = opaque; + SWVTAddressSpace *swvt_as; + + assert(0 <= devfn && devfn < PCI_DEVFN_MAX); + + swvt_as = iommu_find_add_as(s, bus, devfn); + return &swvt_as->as; +} + +static uint64_t piu0_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t ret = 0; + switch (addr) { + default: + break; + } + return ret; +} + +static void piu0_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + SW64IOMMUState *s = (SW64IOMMUState *)opaque; + + switch (addr) { + case 0xb000: + /* DTBaseAddr */ + s->dtbr = val; + break; + case 0xb280: + /* PTLB_FlushVAddr */ + swvt_address_space_invalidate_iova(s, val); + break; + default: + break; + } +} + +const MemoryRegionOps core3_pci_piu0_ops = { + .read = piu0_read, + .write = piu0_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +void sw64_vt_iommu_init(PCIBus *b) +{ + DeviceState *dev_iommu; + SW64IOMMUState *s; + MemoryRegion *io_piu0 = g_new(MemoryRegion, 1); + + dev_iommu = qdev_new(TYPE_SW64_IOMMU); + s = SW64_IOMMU(dev_iommu); + + s->pci_bus = b; + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev_iommu), &error_fatal); + + pci_setup_iommu(b, sw64_dma_iommu, dev_iommu); + + memory_region_init_io(io_piu0, OBJECT(s), &core3_pci_piu0_ops, s, + "pci0-piu0-io", 4 * 1024 * 1024); + memory_region_add_subregion(get_system_memory(), 0x880200000000ULL, + io_piu0); +} + +static int swvt_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, + IOMMUNotifierFlag old, + IOMMUNotifierFlag new, + Error **errp) +{ + SWVTAddressSpace *swvt_as = container_of(iommu, SWVTAddressSpace, iommu); + SW64IOMMUState *s = swvt_as->iommu_state; + + /* Update per-address-space notifier flags */ + swvt_as->notifier_flags = new; + + if (new & IOMMU_NOTIFIER_DEVIOTLB_UNMAP) { + error_setg(errp, "swvt does not support dev-iotlb yet"); + return -EINVAL; + } + + if (old == IOMMU_NOTIFIER_NONE) { + QLIST_INSERT_HEAD(&s->swvt_as_with_notifiers, swvt_as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(swvt_as, next); + } + return 0; +} + +static void swvt_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) +{ + SWVTAddressSpace *swvt_as = container_of(iommu_mr, SWVTAddressSpace, iommu); + + /* + * The replay can be triggered by either a invalidation or a newly + * created entry. No matter what, we release existing mappings + * (it means flushing caches for UNMAP-only registers). + */ + swvt_address_space_unmap(swvt_as, n); +} + +/* GHashTable functions */ +static gboolean swvt_uint64_equal(gconstpointer v1, gconstpointer v2) +{ + return *((const uint64_t *)v1) == *((const uint64_t *)v2); +} + +static guint swvt_uint64_hash(gconstpointer v) +{ + return (guint)*(const uint64_t *)v; +} + +static void iommu_realize(DeviceState *d, Error **errp) +{ + SW64IOMMUState *s = SW64_IOMMU(d); + + QLIST_INIT(&s->swvt_as_with_notifiers); + qemu_mutex_init(&s->iommu_lock); + + s->dtiotlb = g_hash_table_new_full(swvt_uint64_hash, swvt_uint64_equal, + g_free, g_free); + s->ptiotlb = g_hash_table_new_full(swvt_uint64_hash, swvt_uint64_equal, + g_free, g_free); + + s->swvtbus_as_by_busptr = g_hash_table_new(NULL, NULL); +} + +static void iommu_reset(DeviceState *d) +{ +} + +static void sw64_iommu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = iommu_reset; + dc->realize = iommu_realize; +} + +static void sw64_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = sw64_translate_iommu; + imrc->notify_flag_changed = swvt_iommu_notify_flag_changed; + imrc->replay = swvt_iommu_replay; +} + +static const TypeInfo sw64_iommu_info = { + .name = TYPE_SW64_IOMMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SW64IOMMUState), + .class_init = sw64_iommu_class_init, + .class_size = sizeof(SW64IOMMUClass), +}; + +static const TypeInfo sw64_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SW64_IOMMU_MEMORY_REGION, + .class_init = sw64_iommu_memory_region_class_init, +}; + +static void sw64_iommu_register_types(void) +{ + type_register_static(&sw64_iommu_info); + type_register_static(&sw64_iommu_memory_region_info); +} + +type_init(sw64_iommu_register_types) diff --git a/hw/sw64/trace-events b/hw/sw64/trace-events new file mode 100644 index 0000000000..1aa744c984 --- /dev/null +++ b/hw/sw64/trace-events @@ -0,0 +1,3 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# pci.c diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 08e1beec85..4590bcc968 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -191,6 +191,9 @@ enum bfd_architecture #define bfd_mach_alpha_ev4 0x10 #define bfd_mach_alpha_ev5 0x20 #define bfd_mach_alpha_ev6 0x30 + bfd_arch_sw_64, /* Dec Sw_64 */ +#define bfd_mach_sw_64 1 +#define bfd_mach_sw_64_core3 1621 bfd_arch_arm, /* Advanced Risc Machines ARM */ #define bfd_mach_arm_unknown 0 #define bfd_mach_arm_2 1 @@ -429,6 +432,7 @@ int print_insn_h8500 (bfd_vma, disassemble_info*); int print_insn_arm_a64 (bfd_vma, disassemble_info*); int print_insn_alpha (bfd_vma, disassemble_info*); disassembler_ftype arc_get_disassembler (int, int); +int print_insn_sw_64 (bfd_vma, disassemble_info*); int print_insn_arm (bfd_vma, disassemble_info*); int print_insn_sparc (bfd_vma, disassemble_info*); int print_insn_big_a29k (bfd_vma, disassemble_info*); diff --git a/include/elf.h b/include/elf.h index 811bf4a1cb..79c188b62f 100644 --- a/include/elf.h +++ b/include/elf.h @@ -207,6 +207,8 @@ typedef struct mips_elf_abiflags_v0 { #define EF_AVR_MACH 0x7F /* Mask for AVR e_flags to get core type */ +#define EM_SW64 0x9916 /* SW64 */ + /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 #define DT_NEEDED 1 @@ -1417,6 +1419,48 @@ typedef struct { #define EF_RISCV_RVE 0x0008 #define EF_RISCV_TSO 0x0010 +/* + SW_64 ELF relocation types + */ +#define EM_SW_64 0x9916 +#define R_SW_64_NONE 0 /* No reloc */ +#define R_SW_64_REFLONG 1 /* Direct 32 bit */ +#define R_SW_64_REFQUAD 2 /* Direct 64 bit */ +#define R_SW_64_GPREL32 3 /* GP relative 32 bit */ +#define R_SW_64_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_SW_64_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_SW_64_GPDISP 6 /* Add displacement to GP */ +#define R_SW_64_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_SW_64_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_SW_64_SREL16 9 /* PC relative 16 bit */ +#define R_SW_64_SREL32 10 /* PC relative 32 bit */ +#define R_SW_64_SREL64 11 /* PC relative 64 bit */ +#define R_SW_64_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_SW_64_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_SW_64_GPREL16 19 /* GP relative 16 bit */ +#define R_SW_64_COPY 24 /* Copy symbol at runtime */ +#define R_SW_64_GLOB_DAT 25 /* Create GOT entry */ +#define R_SW_64_JMP_SLOT 26 /* Create PLT entry */ +#define R_SW_64_RELATIVE 27 /* Adjust by program base */ +#define R_SW_64_TLS_GD_HI 28 +#define R_SW_64_TLSGD 29 +#define R_SW_64_TLS_LDM 30 +#define R_SW_64_DTPMOD64 31 +#define R_SW_64_GOTDTPREL 32 +#define R_SW_64_DTPREL64 33 +#define R_SW_64_DTPRELHI 34 +#define R_SW_64_DTPRELLO 35 +#define R_SW_64_DTPREL16 36 +#define R_SW_64_GOTTPREL 37 +#define R_SW_64_TPREL64 38 +#define R_SW_64_TPRELHI 39 +#define R_SW_64_TPRELLO 40 +#define R_SW_64_TPREL16 41 +/* Keep this the last entry. */ +#define R_SW_64_NUM 46 +/* Legal values for sh_flags field of Elf64_Shdr. */ +#define SHF_SW_64_GPREL 0x10000000 + typedef struct elf32_rel { Elf32_Addr r_offset; Elf32_Word r_info; diff --git a/include/hw/sw64/sw64_iommu.h b/include/hw/sw64/sw64_iommu.h new file mode 100644 index 0000000000..7191876083 --- /dev/null +++ b/include/hw/sw64/sw64_iommu.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021-2025 Wuxi Institute of Advanced Technology + * Written by Lu Feifei + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#ifndef HW_SW64_IOMMU_H +#define HW_SW64_IOMMU_H + +#include "hw/sysbus.h" +#include "hw/pci/pci.h" + +#define TYPE_SW64_IOMMU_MEMORY_REGION "sw64-iommu-memory-region" +#define SW_IOMMU_ENTRY_VALID ((1UL) << 63) +#define SW_IOMMU_LEVEL1_OFFSET 0x1ff +#define SW_IOMMU_LEVEL2_OFFSET 0x3ff +#define SW_IOMMU_ENABLE 3 +#define SW_IOMMU_GRN ((0UL) << 4) +#define SWVT_PCI_BUS_MAX 256 + +typedef struct SW64IOMMUClass SW64IOMMUClass; +typedef struct SW64IOMMUState SW64IOMMUState; +typedef struct SWVTAddressSpace SWVTAddressSpace; +typedef struct SW64DTIOTLBKey SW64DTIOTLBKey; +typedef struct SW64PTIOTLBKey SW64PTIOTLBKey; +typedef struct SW64DTIOTLBEntry SW64DTIOTLBEntry; +typedef struct SWVTBus SWVTBus; + +struct SW64DTIOTLBEntry { + uint16_t source_id; + unsigned long ptbase_addr; +}; + +struct SW64DTIOTLBKey { + uint16_t source_id; +}; + +struct SW64PTIOTLBKey { + uint16_t source_id; + dma_addr_t iova; +}; + +struct SWVTAddressSpace { + PCIBus *bus; + uint8_t devfn; + AddressSpace as; + IOMMUMemoryRegion iommu; + MemoryRegion root; + MemoryRegion msi; /* Interrupt region: 0xfeeXXXXX */ + SW64IOMMUState *iommu_state; + QLIST_ENTRY(SWVTAddressSpace) next; + /* Superset of notifier flags that this address space has */ + IOMMUNotifierFlag notifier_flags; +}; + +struct SWVTBus { + PCIBus* bus; /* A reference to the bus to provide translation for */ + SWVTAddressSpace *dev_as[0]; /* A table of SWVTAddressSpace objects indexed by devfn */ +}; + +struct SW64IOMMUState { + SysBusDevice busdev; + dma_addr_t dtbr; /* Current root table pointer */ + GHashTable *dtiotlb; /* IOTLB for device table */ + GHashTable *ptiotlb; /* IOTLB for page table */ + + GHashTable *swvtbus_as_by_busptr; + /* list of registered notifiers */ + QLIST_HEAD(, SWVTAddressSpace) swvt_as_with_notifiers; + + PCIBus *pci_bus; + QemuMutex iommu_lock; +}; + +struct SW64IOMMUClass { + SysBusDeviceClass parent; + DeviceRealize realize; +}; + +#define TYPE_SW64_IOMMU "sw64-iommu" +#define SW64_IOMMU(obj) \ + OBJECT_CHECK(SW64IOMMUState, (obj), TYPE_SW64_IOMMU) +#define SW64_IOMMU_CLASS(klass) \ + OBJECT_CLASS_CHECK(SW64IOMMUClass, (klass), TYPE_SW64_IOMMU) +#define SW64_IOMMU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SW64IOMMUClass, (obj), TYPE_SW64_IOMMU) +extern void sw64_vt_iommu_init(PCIBus *b); +extern void swvt_address_space_invalidate_iova(SW64IOMMUState *s, unsigned long val); +extern void swvt_address_space_unmap_iova(SW64IOMMUState *s, unsigned long val); +extern void swvt_address_space_map_iova(SW64IOMMUState *s, unsigned long val); +extern SWVTAddressSpace *iommu_find_add_as(SW64IOMMUState *s, PCIBus *bus, int devfn); +extern MemTxResult msi_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, + MemTxAttrs attrs); +#endif diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 112a29910b..6141122308 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -85,6 +85,8 @@ #define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); }) #elif defined(__alpha__) #define smp_read_barrier_depends() asm volatile("mb":::"memory") +#elif defined(__sw_64__) +#define smp_read_barrier_depends() asm volatile("memb":::"memory") #else #define smp_read_barrier_depends() barrier() #endif diff --git a/include/qemu/timer.h b/include/qemu/timer.h index d263fad9a4..e6d442abee 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -1007,6 +1007,16 @@ static inline int64_t cpu_get_host_ticks(void) return cur - ofs; } +#elif defined(__sw_64__) + +static inline int64_t cpu_get_host_ticks(void) +{ + uint64_t cc; + + asm volatile("rtc %0" : "=r"(cc)); + return cc; +} + #else /* The host CPU doesn't have an easily accessible cycle counter. Just return a monotonically increasing value. This will be diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 70c579560a..1cf27baa7c 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -24,6 +24,7 @@ enum { QEMU_ARCH_RX = (1 << 20), QEMU_ARCH_AVR = (1 << 21), QEMU_ARCH_HEXAGON = (1 << 22), + QEMU_ARCH_SW64 = (1 << 23), }; extern const uint32_t arch_type; diff --git a/linux-headers/asm-sw64/kvm.h b/linux-headers/asm-sw64/kvm.h new file mode 100644 index 0000000000..b0ce2ca346 --- /dev/null +++ b/linux-headers/asm-sw64/kvm.h @@ -0,0 +1,122 @@ +#ifndef __LINUX_KVM_SW64_H +#define __LINUX_KVM_SW64_H + +#include +/* + * for KVM_GET_REGS and KVM_SET_REGS + */ +struct kvm_regs { + unsigned long r0; + unsigned long r1; + unsigned long r2; + unsigned long r3; + + unsigned long r4; + unsigned long r5; + unsigned long r6; + unsigned long r7; + + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r11; + + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + + unsigned long r19; + unsigned long r20; + unsigned long r21; + unsigned long r22; + + unsigned long r23; + unsigned long r24; + unsigned long r25; + unsigned long r26; + + unsigned long r27; + unsigned long r28; + unsigned long __padding0; + unsigned long fpcr; + + unsigned long fp[124]; + /* These are saved by hmcode: */ + unsigned long ps; + unsigned long pc; + unsigned long gp; + unsigned long r16; + unsigned long r17; + unsigned long r18; +}; + +struct vcpucb { + unsigned long go_flag; + unsigned long pcbb; + unsigned long ksp; + unsigned long usp; + unsigned long kgp; + unsigned long ent_arith; + unsigned long ent_if; + unsigned long ent_int; + unsigned long ent_mm; + unsigned long ent_sys; + unsigned long ent_una; + unsigned long stack_pc; + unsigned long new_a0; + unsigned long new_a1; + unsigned long new_a2; + unsigned long whami; + unsigned long csr_save; + unsigned long wakeup_magic; + unsigned long host_vcpucb; + unsigned long upcr; + unsigned long vpcr; + unsigned long dtb_pcr; + unsigned long guest_ksp; + unsigned long guest_usp; + unsigned long vcpu_irq_disabled; + unsigned long vcpu_irq; + unsigned long ptbr; + unsigned long int_stat0; + unsigned long int_stat1; + unsigned long int_stat2; + unsigned long int_stat3; + unsigned long reset_entry; + unsigned long pvcpu; + unsigned long exit_reason; + unsigned long ipaddr; + unsigned long vcpu_irq_vector; +}; + +/* + * for KVM_GET_FPU and KVM_SET_FPU + */ +struct kvm_fpu { +}; + +/* + * KVM SW_64 specific structures and definitions + */ +struct kvm_debug_exit_arch { +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { +}; + +/* definition of registers in kvm_run */ +struct kvm_sync_regs { +}; + +/* dummy definition */ +struct kvm_sregs { +}; + +#define KVM_SW64_VCPU_INIT _IO(KVMIO, 0xba) +#define KVM_SW64_USE_SLAVE _IO(KVMIO, 0xbb) +#define KVM_SW64_GET_VCB _IO(KVMIO, 0xbc) +#define KVM_SW64_SET_VCB _IO(KVMIO, 0xbd) + +#endif /* __LINUX_KVM_SW64_H */ diff --git a/linux-headers/asm-sw64/unistd.h b/linux-headers/asm-sw64/unistd.h new file mode 100644 index 0000000000..affe297e73 --- /dev/null +++ b/linux-headers/asm-sw64/unistd.h @@ -0,0 +1,380 @@ +#ifndef _UAPI_ASM_SW64_UNISTD_64_H +#define _UAPI_ASM_SW64_UNISTD_64_H + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_close 6 +#define __NR_osf_wait4 7 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_chdir 12 +#define __NR_fchdir 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_chown 16 +#define __NR_brk 17 +#define __NR_lseek 19 +#define __NR_getxpid 20 +#define __NR_osf_mount 21 +#define __NR_umount2 22 +#define __NR_setuid 23 +#define __NR_getxuid 24 +#define __NR_ptrace 26 +#define __NR_access 33 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_setpgid 39 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_osf_set_program_attributes 43 +#define __NR_open 45 +#define __NR_getxgid 47 +#define __NR_osf_sigprocmask 48 +#define __NR_acct 51 +#define __NR_sigpending 52 +#define __NR_ioctl 54 +#define __NR_symlink 57 +#define __NR_readlink 58 +#define __NR_execve 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_getpgrp 63 +#define __NR_getpagesize 64 +#define __NR_vfork 66 +#define __NR_stat 67 +#define __NR_lstat 68 +#define __NR_mmap 71 +#define __NR_munmap 73 +#define __NR_mprotect 74 +#define __NR_madvise 75 +#define __NR_vhangup 76 +#define __NR_getgroups 79 +#define __NR_setgroups 80 +#define __NR_setpgrp 82 +#define __NR_osf_setitimer 83 +#define __NR_osf_getitimer 86 +#define __NR_gethostname 87 +#define __NR_sethostname 88 +#define __NR_getdtablesize 89 +#define __NR_dup2 90 +#define __NR_fstat 91 +#define __NR_fcntl 92 +#define __NR_osf_select 93 +#define __NR_poll 94 +#define __NR_fsync 95 +#define __NR_setpriority 96 +#define __NR_socket 97 +#define __NR_connect 98 +#define __NR_accept 99 +#define __NR_getpriority 100 +#define __NR_send 101 +#define __NR_recv 102 +#define __NR_sigreturn 103 +#define __NR_bind 104 +#define __NR_setsockopt 105 +#define __NR_listen 106 +#define __NR_sigsuspend 111 +#define __NR_osf_sigstack 112 +#define __NR_recvmsg 113 +#define __NR_sendmsg 114 +#define __NR_osf_gettimeofday 116 +#define __NR_osf_getrusage 117 +#define __NR_getsockopt 118 +#define __NR_socketcall 119 +#define __NR_readv 120 +#define __NR_writev 121 +#define __NR_osf_settimeofday 122 +#define __NR_fchown 123 +#define __NR_fchmod 124 +#define __NR_recvfrom 125 +#define __NR_setreuid 126 +#define __NR_setregid 127 +#define __NR_rename 128 +#define __NR_truncate 129 +#define __NR_ftruncate 130 +#define __NR_flock 131 +#define __NR_setgid 132 +#define __NR_sendto 133 +#define __NR_shutdown 134 +#define __NR_socketpair 135 +#define __NR_mkdir 136 +#define __NR_rmdir 137 +#define __NR_osf_utimes 138 +#define __NR_getpeername 141 +#define __NR_getrlimit 144 +#define __NR_setrlimit 145 +#define __NR_setsid 147 +#define __NR_quotactl 148 +#define __NR_getsockname 150 +#define __NR_sigaction 156 +#define __NR_osf_getdirentries 159 +#define __NR_osf_statfs 160 +#define __NR_osf_fstatfs 161 +#define __NR_osf_getdomainname 165 +#define __NR_setdomainname 166 +#define __NR_bpf 170 +#define __NR_userfaultfd 171 +#define __NR_membarrier 172 +#define __NR_mlock2 173 +#define __NR_getpid 174 +#define __NR_getppid 175 +#define __NR_getuid 176 +#define __NR_geteuid 177 +#define __NR_getgid 178 +#define __NR_getegid 179 +#define __NR_osf_swapon 199 +#define __NR_msgctl 200 +#define __NR_msgget 201 +#define __NR_msgrcv 202 +#define __NR_msgsnd 203 +#define __NR_semctl 204 +#define __NR_semget 205 +#define __NR_semop 206 +#define __NR_osf_utsname 207 +#define __NR_lchown 208 +#define __NR_shmat 209 +#define __NR_shmctl 210 +#define __NR_shmdt 211 +#define __NR_shmget 212 +#define __NR_msync 217 +#define __NR_osf_stat 224 +#define __NR_osf_lstat 225 +#define __NR_osf_fstat 226 +#define __NR_osf_statfs64 227 +#define __NR_osf_fstatfs64 228 +#define __NR_statfs64 229 +#define __NR_fstatfs64 230 +#define __NR_getpgid 233 +#define __NR_getsid 234 +#define __NR_sigaltstack 235 +#define __NR_osf_sysinfo 241 +#define __NR_osf_proplist_syscall 244 +#define __NR_osf_usleep_thread 251 +#define __NR_sysfs 254 +#define __NR_osf_getsysinfo 256 +#define __NR_osf_setsysinfo 257 +#define __NR_bdflush 300 +#define __NR_sethae 301 +#define __NR_mount 302 +#define __NR_old_adjtimex 303 +#define __NR_swapoff 304 +#define __NR_getdents 305 +#define __NR_create_module 306 +#define __NR_init_module 307 +#define __NR_delete_module 308 +#define __NR_get_kernel_syms 309 +#define __NR_syslog 310 +#define __NR_reboot 311 +#define __NR_clone 312 +#define __NR_uselib 313 +#define __NR_mlock 314 +#define __NR_munlock 315 +#define __NR_mlockall 316 +#define __NR_munlockall 317 +#define __NR_sysinfo 318 +#define __NR__sysctl 319 +#define __NR_oldumount 321 +#define __NR_swapon 322 +#define __NR_times 323 +#define __NR_personality 324 +#define __NR_setfsuid 325 +#define __NR_setfsgid 326 +#define __NR_ustat 327 +#define __NR_statfs 328 +#define __NR_fstatfs 329 +#define __NR_sched_setparam 330 +#define __NR_sched_getparam 331 +#define __NR_sched_setscheduler 332 +#define __NR_sched_getscheduler 333 +#define __NR_sched_yield 334 +#define __NR_sched_get_priority_max 335 +#define __NR_sched_get_priority_min 336 +#define __NR_sched_rr_get_interval 337 +#define __NR_afs_syscall 338 +#define __NR_uname 339 +#define __NR_nanosleep 340 +#define __NR_mremap 341 +#define __NR_nfsservctl 342 +#define __NR_setresuid 343 +#define __NR_getresuid 344 +#define __NR_pciconfig_read 345 +#define __NR_pciconfig_write 346 +#define __NR_query_module 347 +#define __NR_prctl 348 +#define __NR_pread64 349 +#define __NR_pwrite64 350 +#define __NR_rt_sigreturn 351 +#define __NR_rt_sigaction 352 +#define __NR_rt_sigprocmask 353 +#define __NR_rt_sigpending 354 +#define __NR_rt_sigtimedwait 355 +#define __NR_rt_sigqueueinfo 356 +#define __NR_rt_sigsuspend 357 +#define __NR_select 358 +#define __NR_gettimeofday 359 +#define __NR_settimeofday 360 +#define __NR_getitimer 361 +#define __NR_setitimer 362 +#define __NR_utimes 363 +#define __NR_getrusage 364 +#define __NR_wait4 365 +#define __NR_adjtimex 366 +#define __NR_getcwd 367 +#define __NR_capget 368 +#define __NR_capset 369 +#define __NR_sendfile 370 +#define __NR_setresgid 371 +#define __NR_getresgid 372 +#define __NR_dipc 373 +#define __NR_pivot_root 374 +#define __NR_mincore 375 +#define __NR_pciconfig_iobase 376 +#define __NR_getdents64 377 +#define __NR_gettid 378 +#define __NR_readahead 379 +#define __NR_tkill 381 +#define __NR_setxattr 382 +#define __NR_lsetxattr 383 +#define __NR_fsetxattr 384 +#define __NR_getxattr 385 +#define __NR_lgetxattr 386 +#define __NR_fgetxattr 387 +#define __NR_listxattr 388 +#define __NR_llistxattr 389 +#define __NR_flistxattr 390 +#define __NR_removexattr 391 +#define __NR_lremovexattr 392 +#define __NR_fremovexattr 393 +#define __NR_futex 394 +#define __NR_sched_setaffinity 395 +#define __NR_sched_getaffinity 396 +#define __NR_tuxcall 397 +#define __NR_io_setup 398 +#define __NR_io_destroy 399 +#define __NR_io_getevents 400 +#define __NR_io_submit 401 +#define __NR_io_cancel 402 +#define __NR_io_pgetevents 403 +#define __NR_rseq 404 +#define __NR_exit_group 405 +#define __NR_lookup_dcookie 406 +#define __NR_epoll_create 407 +#define __NR_epoll_ctl 408 +#define __NR_epoll_wait 409 +#define __NR_remap_file_pages 410 +#define __NR_set_tid_address 411 +#define __NR_restart_syscall 412 +#define __NR_fadvise64 413 +#define __NR_timer_create 414 +#define __NR_timer_settime 415 +#define __NR_timer_gettime 416 +#define __NR_timer_getoverrun 417 +#define __NR_timer_delete 418 +#define __NR_clock_settime 419 +#define __NR_clock_gettime 420 +#define __NR_clock_getres 421 +#define __NR_clock_nanosleep 422 +#define __NR_semtimedop 423 +#define __NR_tgkill 424 +#define __NR_stat64 425 +#define __NR_lstat64 426 +#define __NR_fstat64 427 +#define __NR_vserver 428 +#define __NR_mbind 429 +#define __NR_get_mempolicy 430 +#define __NR_set_mempolicy 431 +#define __NR_mq_open 432 +#define __NR_mq_unlink 433 +#define __NR_mq_timedsend 434 +#define __NR_mq_timedreceive 435 +#define __NR_mq_notify 436 +#define __NR_mq_getsetattr 437 +#define __NR_waitid 438 +#define __NR_add_key 439 +#define __NR_request_key 440 +#define __NR_keyctl 441 +#define __NR_ioprio_set 442 +#define __NR_ioprio_get 443 +#define __NR_inotify_init 444 +#define __NR_inotify_add_watch 445 +#define __NR_inotify_rm_watch 446 +#define __NR_fdatasync 447 +#define __NR_kexec_load 448 +#define __NR_migrate_pages 449 +#define __NR_openat 450 +#define __NR_mkdirat 451 +#define __NR_mknodat 452 +#define __NR_fchownat 453 +#define __NR_futimesat 454 +#define __NR_fstatat64 455 +#define __NR_unlinkat 456 +#define __NR_renameat 457 +#define __NR_linkat 458 +#define __NR_symlinkat 459 +#define __NR_readlinkat 460 +#define __NR_fchmodat 461 +#define __NR_faccessat 462 +#define __NR_pselect6 463 +#define __NR_ppoll 464 +#define __NR_unshare 465 +#define __NR_set_robust_list 466 +#define __NR_get_robust_list 467 +#define __NR_splice 468 +#define __NR_sync_file_range 469 +#define __NR_tee 470 +#define __NR_vmsplice 471 +#define __NR_move_pages 472 +#define __NR_getcpu 473 +#define __NR_epoll_pwait 474 +#define __NR_utimensat 475 +#define __NR_signalfd 476 +#define __NR_timerfd 477 +#define __NR_eventfd 478 +#define __NR_recvmmsg 479 +#define __NR_fallocate 480 +#define __NR_timerfd_create 481 +#define __NR_timerfd_settime 482 +#define __NR_timerfd_gettime 483 +#define __NR_signalfd4 484 +#define __NR_eventfd2 485 +#define __NR_epoll_create1 486 +#define __NR_dup3 487 +#define __NR_pipe2 488 +#define __NR_inotify_init1 489 +#define __NR_preadv 490 +#define __NR_pwritev 491 +#define __NR_rt_tgsigqueueinfo 492 +#define __NR_perf_event_open 493 +#define __NR_fanotify_init 494 +#define __NR_fanotify_mark 495 +#define __NR_prlimit64 496 +#define __NR_name_to_handle_at 497 +#define __NR_open_by_handle_at 498 +#define __NR_clock_adjtime 499 +#define __NR_syncfs 500 +#define __NR_setns 501 +#define __NR_accept4 502 +#define __NR_sendmmsg 503 +#define __NR_process_vm_readv 504 +#define __NR_process_vm_writev 505 +#define __NR_kcmp 506 +#define __NR_finit_module 507 +#define __NR_sched_setattr 508 +#define __NR_sched_getattr 509 +#define __NR_renameat2 510 +#define __NR_getrandom 511 +#define __NR_memfd_create 512 +#define __NR_execveat 513 +#define __NR_seccomp 514 +#define __NR_copy_file_range 515 +#define __NR_preadv2 516 +#define __NR_pwritev2 517 +#define __NR_statx 518 + +#ifdef __KERNEL__ +#define __NR_syscalls 519 +#endif + +#endif /* _UAPI_ASM_SW64_UNISTD_64_H */ diff --git a/linux-user/meson.build b/linux-user/meson.build index bf62c13e37..4f4196ed13 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -37,5 +37,6 @@ subdir('ppc') subdir('s390x') subdir('sh4') subdir('sparc') +subdir('sw64') subdir('x86_64') subdir('xtensa') diff --git a/linux-user/sw64/cpu_loop.c b/linux-user/sw64/cpu_loop.c new file mode 100644 index 0000000000..3f2fde0fba --- /dev/null +++ b/linux-user/sw64/cpu_loop.c @@ -0,0 +1,108 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUSW64State *env) +{ + CPUState *cs = CPU(sw64_env_get_cpu(env)); + int trapnr; + target_siginfo_t info; + abi_long sysret; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_OPCDEC: + cpu_abort(cs, "ILLEGAL SW64 insn at line %d!", __LINE__); + case EXCP_CALL_SYS: + switch (env->error_code) { + case 0x83: + /* CALLSYS */ + trapnr = env->ir[IDX_V0]; + sysret = do_syscall(env, trapnr, + env->ir[IDX_A0], env->ir[IDX_A1], + env->ir[IDX_A2], env->ir[IDX_A3], + env->ir[IDX_A4], env->ir[IDX_A5], + 0, 0); + if (sysret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + break; + } + if (sysret == -TARGET_QEMU_ESIGRETURN) { + break; + } + /* Syscall writes 0 to V0 to bypass error check, similar + to how this is handled internal to Linux kernel. + (Ab)use trapnr temporarily as boolean indicating error. */ + trapnr = (env->ir[IDX_V0] != 0 && sysret < 0); + env->ir[IDX_V0] = (trapnr ? -sysret : sysret); + env->ir[IDX_A3] = trapnr; + break; + default: + printf("UNDO sys_call %lx\n", env->error_code); + exit(-1); + } + break; + case EXCP_MMFAULT: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID + ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); + info._sifields._sigfault._addr = env->trap_arg0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ARITH: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTINV; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + default: + cpu_abort(cs, "UNDO"); + } + process_pending_signals (env); + + /* Most of the traps imply a transition through HMcode, which + implies an REI instruction has been executed. Which means + that RX and LOCK_ADDR should be cleared. But there are a + few exceptions for traps internal to QEMU. */ + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + + for(i = 0; i < 28; i++) { + env->ir[i] = ((abi_ulong *)regs)[i]; + } + env->ir[IDX_SP] = regs->usp; + env->pc = regs->pc; +} diff --git a/linux-user/sw64/signal.c b/linux-user/sw64/signal.c new file mode 100644 index 0000000000..5822e808d3 --- /dev/null +++ b/linux-user/sw64/signal.c @@ -0,0 +1,273 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_long sc_onstack; + abi_long sc_mask; + abi_long sc_pc; + abi_long sc_ps; + abi_long sc_regs[32]; + abi_long sc_ownedfp; + abi_long sc_fpregs[32]; + abi_ulong sc_fpcr; + abi_ulong sc_fp_control; + abi_ulong sc_reserved1; + abi_ulong sc_reserved2; + abi_ulong sc_ssize; + abi_ulong sc_sbase; + abi_ulong sc_traparg_a0; + abi_ulong sc_traparg_a1; + abi_ulong sc_traparg_a2; + abi_ulong sc_fp_trap_pc; + abi_ulong sc_fp_trigger_sum; + abi_ulong sc_fp_trigger_inst; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + abi_ulong tuc_osf_sigmask; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_sigframe { + struct target_sigcontext sc; + unsigned int retcode[3]; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + unsigned int retcode[3]; +}; + +#define INSN_MOV_R30_R16 0x47fe0410 +#define INSN_LDI_R0 0x201f0000 +#define INSN_CALLSYS 0x00000083 + +static void setup_sigcontext(struct target_sigcontext *sc, CPUSW64State *env, + abi_ulong frame_addr, target_sigset_t *set) +{ + int i; + + __put_user(on_sig_stack(frame_addr), &sc->sc_onstack); + __put_user(set->sig[0], &sc->sc_mask); + __put_user(env->pc, &sc->sc_pc); + __put_user(8, &sc->sc_ps); + + for (i = 0; i < 31; ++i) { + __put_user(env->ir[i], &sc->sc_regs[i]); + } + __put_user(0, &sc->sc_regs[31]); + + for (i = 0; i < 31; ++i) { + __put_user(env->fr[i], &sc->sc_fpregs[i]); + } + __put_user(0, &sc->sc_fpregs[31]); + __put_user(cpu_sw64_load_fpcr(env), &sc->sc_fpcr); + + __put_user(0, &sc->sc_traparg_a0); /* FIXME */ + __put_user(0, &sc->sc_traparg_a1); /* FIXME */ + __put_user(0, &sc->sc_traparg_a2); /* FIXME */ +} + +static void restore_sigcontext(CPUSW64State *env, + struct target_sigcontext *sc) +{ + uint64_t fpcr; + int i; + + __get_user(env->pc, &sc->sc_pc); + + for (i = 0; i < 31; ++i) { + __get_user(env->ir[i], &sc->sc_regs[i]); + } + for (i = 0; i < 31; ++i) { + __get_user(env->fr[i], &sc->sc_fpregs[i]); + } + + __get_user(fpcr, &sc->sc_fpcr); + cpu_sw64_store_fpcr(env, fpcr); +} + +static inline abi_ulong get_sigframe(struct target_sigaction *sa, + CPUSW64State *env, + unsigned long framesize) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), sa); + + return (sp - framesize) & -32; +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSW64State *env) +{ + abi_ulong frame_addr, r26; + struct target_sigframe *frame; + int err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + setup_sigcontext(&frame->sc, env, frame_addr, set); + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, + &frame->retcode[1]); + __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb() */ + r26 = frame_addr + offsetof(struct target_sigframe, retcode); + } + + unlock_user_struct(frame, frame_addr, 1); + + if (err) { +give_sigsegv: + force_sigsegv(sig); + return; + } + + env->ir[IDX_RA] = r26; + env->ir[IDX_PV] = env->pc = ka->_sa_handler; + env->ir[IDX_A0] = sig; + env->ir[IDX_A1] = 0; + env->ir[IDX_A2] = frame_addr + offsetof(struct target_sigframe, sc); + env->ir[IDX_SP] = frame_addr; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSW64State *env) +{ + abi_ulong frame_addr, r26; + struct target_rt_sigframe *frame; + int i, err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); + + target_save_altstack(&frame->uc.tuc_stack, env); + + setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, + &frame->retcode[1]); + __put_user(INSN_CALLSYS, &frame->retcode[2]); + r26 = frame_addr + offsetof(struct target_sigframe, retcode); + } + + if (err) { +give_sigsegv: + force_sigsegv(sig); + return; + } + + env->ir[IDX_RA] = r26; + env->ir[IDX_PV] = env->pc = ka->_sa_handler; + env->ir[IDX_A0] = sig; + env->ir[IDX_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->ir[IDX_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->ir[IDX_SP] = frame_addr; +} + +long do_sigreturn(CPUSW64State *env) +{ + struct target_sigcontext *sc; + abi_ulong sc_addr = env->ir[IDX_A0]; + target_sigset_t target_set; + sigset_t set; + + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { + goto badframe; + } + + target_sigemptyset(&target_set); + __get_user(target_set.sig[0], &sc->sc_mask); + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(env, sc); + unlock_user_struct(sc, sc_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUSW64State *env) +{ + abi_ulong frame_addr = env->ir[IDX_A0]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->ir[IDX_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/sw64/sockbits.h b/linux-user/sw64/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/sw64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/sw64/syscall_nr.h b/linux-user/sw64/syscall_nr.h new file mode 100644 index 0000000000..91737af322 --- /dev/null +++ b/linux-user/sw64/syscall_nr.h @@ -0,0 +1,471 @@ +/* + * This file contains the system call numbers. + */ +#define TARGET_NR_osf_syscall 0 /* not implemented */ +#define TARGET_NR_exit 1 +#define TARGET_NR_fork 2 +#define TARGET_NR_read 3 +#define TARGET_NR_write 4 +#define TARGET_NR_osf_old_open 5 /* not implemented */ +#define TARGET_NR_close 6 +#define TARGET_NR_osf_wait4 7 +#define TARGET_NR_osf_old_creat 8 /* not implemented */ +#define TARGET_NR_link 9 +#define TARGET_NR_unlink 10 +#define TARGET_NR_osf_execve 11 /* not implemented */ +#define TARGET_NR_chdir 12 +#define TARGET_NR_fchdir 13 +#define TARGET_NR_mknod 14 +#define TARGET_NR_chmod 15 +#define TARGET_NR_chown 16 +#define TARGET_NR_brk 17 +#define TARGET_NR_osf_getfsstat 18 /* not implemented */ +#define TARGET_NR_lseek 19 +#define TARGET_NR_getxpid 20 +#define TARGET_NR_osf_mount 21 +#define TARGET_NR_umount 22 +#define TARGET_NR_setuid 23 +#define TARGET_NR_getxuid 24 +#define TARGET_NR_exec_with_loader 25 /* not implemented */ +#define TARGET_NR_ptrace 26 +#define TARGET_NR_osf_nrecvmsg 27 /* not implemented */ +#define TARGET_NR_osf_nsendmsg 28 /* not implemented */ +#define TARGET_NR_osf_nrecvfrom 29 /* not implemented */ +#define TARGET_NR_osf_naccept 30 /* not implemented */ +#define TARGET_NR_osf_ngetpeername 31 /* not implemented */ +#define TARGET_NR_osf_ngetsockname 32 /* not implemented */ +#define TARGET_NR_access 33 +#define TARGET_NR_osf_chflags 34 /* not implemented */ +#define TARGET_NR_osf_fchflags 35 /* not implemented */ +#define TARGET_NR_sync 36 +#define TARGET_NR_kill 37 +#define TARGET_NR_osf_old_stat 38 /* not implemented */ +#define TARGET_NR_setpgid 39 +#define TARGET_NR_osf_old_lstat 40 /* not implemented */ +#define TARGET_NR_dup 41 +#define TARGET_NR_pipe 42 +#define TARGET_NR_osf_set_program_attributes 43 +#define TARGET_NR_osf_profil 44 /* not implemented */ +#define TARGET_NR_open 45 +#define TARGET_NR_osf_old_sigaction 46 /* not implemented */ +#define TARGET_NR_getxgid 47 +#define TARGET_NR_osf_sigprocmask 48 +#define TARGET_NR_osf_getlogin 49 /* not implemented */ +#define TARGET_NR_osf_setlogin 50 /* not implemented */ +#define TARGET_NR_acct 51 +#define TARGET_NR_sigpending 52 + +#define TARGET_NR_ioctl 54 +#define TARGET_NR_osf_reboot 55 /* not implemented */ +#define TARGET_NR_osf_revoke 56 /* not implemented */ +#define TARGET_NR_symlink 57 +#define TARGET_NR_readlink 58 +#define TARGET_NR_execve 59 +#define TARGET_NR_umask 60 +#define TARGET_NR_chroot 61 +#define TARGET_NR_osf_old_fstat 62 /* not implemented */ +#define TARGET_NR_getpgrp 63 +#define TARGET_NR_getpagesize 64 +#define TARGET_NR_osf_mremap 65 /* not implemented */ +#define TARGET_NR_vfork 66 +#define TARGET_NR_stat 67 +#define TARGET_NR_lstat 68 +#define TARGET_NR_osf_sbrk 69 /* not implemented */ +#define TARGET_NR_osf_sstk 70 /* not implemented */ +#define TARGET_NR_mmap 71 /* OSF/1 mmap is superset of Linux */ +#define TARGET_NR_osf_old_vadvise 72 /* not implemented */ +#define TARGET_NR_munmap 73 +#define TARGET_NR_mprotect 74 +#define TARGET_NR_madvise 75 +#define TARGET_NR_vhangup 76 +#define TARGET_NR_osf_kmodcall 77 /* not implemented */ +#define TARGET_NR_osf_mincore 78 /* not implemented */ +#define TARGET_NR_getgroups 79 +#define TARGET_NR_setgroups 80 +#define TARGET_NR_osf_old_getpgrp 81 /* not implemented */ +#define TARGET_NR_setpgrp 82 /* BSD alias for setpgid */ +#define TARGET_NR_osf_setitimer 83 +#define TARGET_NR_osf_old_wait 84 /* not implemented */ +#define TARGET_NR_osf_table 85 /* not implemented */ +#define TARGET_NR_osf_getitimer 86 +#define TARGET_NR_gethostname 87 +#define TARGET_NR_sethostname 88 +#define TARGET_NR_getdtablesize 89 +#define TARGET_NR_dup2 90 +#define TARGET_NR_fstat 91 +#define TARGET_NR_fcntl 92 +#define TARGET_NR_osf_select 93 +#define TARGET_NR_poll 94 +#define TARGET_NR_fsync 95 +#define TARGET_NR_setpriority 96 +#define TARGET_NR_socket 97 +#define TARGET_NR_connect 98 +#define TARGET_NR_accept 99 +#define TARGET_NR_getpriority 100 +#define TARGET_NR_send 101 +#define TARGET_NR_recv 102 +#define TARGET_NR_sigreturn 103 +#define TARGET_NR_bind 104 +#define TARGET_NR_setsockopt 105 +#define TARGET_NR_listen 106 +#define TARGET_NR_osf_plock 107 /* not implemented */ +#define TARGET_NR_osf_old_sigvec 108 /* not implemented */ +#define TARGET_NR_osf_old_sigblock 109 /* not implemented */ +#define TARGET_NR_osf_old_sigsetmask 110 /* not implemented */ +#define TARGET_NR_sigsuspend 111 +#define TARGET_NR_osf_sigstack 112 +#define TARGET_NR_recvmsg 113 +#define TARGET_NR_sendmsg 114 +#define TARGET_NR_osf_old_vtrace 115 /* not implemented */ +#define TARGET_NR_osf_gettimeofday 116 +#define TARGET_NR_osf_getrusage 117 +#define TARGET_NR_getsockopt 118 + +#define TARGET_NR_readv 120 +#define TARGET_NR_writev 121 +#define TARGET_NR_osf_settimeofday 122 +#define TARGET_NR_fchown 123 +#define TARGET_NR_fchmod 124 +#define TARGET_NR_recvfrom 125 +#define TARGET_NR_setreuid 126 +#define TARGET_NR_setregid 127 +#define TARGET_NR_rename 128 +#define TARGET_NR_truncate 129 +#define TARGET_NR_ftruncate 130 +#define TARGET_NR_flock 131 +#define TARGET_NR_setgid 132 +#define TARGET_NR_sendto 133 +#define TARGET_NR_shutdown 134 +#define TARGET_NR_socketpair 135 +#define TARGET_NR_mkdir 136 +#define TARGET_NR_rmdir 137 +#define TARGET_NR_osf_utimes 138 +#define TARGET_NR_osf_old_sigreturn 139 /* not implemented */ +#define TARGET_NR_osf_adjtime 140 /* not implemented */ +#define TARGET_NR_getpeername 141 +#define TARGET_NR_osf_gethostid 142 /* not implemented */ +#define TARGET_NR_osf_sethostid 143 /* not implemented */ +#define TARGET_NR_getrlimit 144 +#define TARGET_NR_setrlimit 145 +#define TARGET_NR_osf_old_killpg 146 /* not implemented */ +#define TARGET_NR_setsid 147 +#define TARGET_NR_quotactl 148 +#define TARGET_NR_osf_oldquota 149 /* not implemented */ +#define TARGET_NR_getsockname 150 + +#define TARGET_NR_osf_pid_block 153 /* not implemented */ +#define TARGET_NR_osf_pid_unblock 154 /* not implemented */ + +#define TARGET_NR_sigaction 156 +#define TARGET_NR_osf_sigwaitprim 157 /* not implemented */ +#define TARGET_NR_osf_nfssvc 158 /* not implemented */ +#define TARGET_NR_osf_getdirentries 159 +#define TARGET_NR_osf_statfs 160 +#define TARGET_NR_osf_fstatfs 161 + +#define TARGET_NR_osf_asynch_daemon 163 /* not implemented */ +#define TARGET_NR_osf_getfh 164 /* not implemented */ +#define TARGET_NR_osf_getdomainname 165 +#define TARGET_NR_setdomainname 166 + +#define TARGET_NR_osf_exportfs 169 /* not implemented */ + +#define TARGET_NR_osf_alt_plock 181 /* not implemented */ + +#define TARGET_NR_osf_getmnt 184 /* not implemented */ + +#define TARGET_NR_osf_alt_sigpending 187 /* not implemented */ +#define TARGET_NR_osf_alt_setsid 188 /* not implemented */ + +#define TARGET_NR_osf_swapon 199 +#define TARGET_NR_msgctl 200 +#define TARGET_NR_msgget 201 +#define TARGET_NR_msgrcv 202 +#define TARGET_NR_msgsnd 203 +#define TARGET_NR_semctl 204 +#define TARGET_NR_semget 205 +#define TARGET_NR_semop 206 +#define TARGET_NR_osf_utsname 207 +#define TARGET_NR_lchown 208 +#define TARGET_NR_osf_shmat 209 +#define TARGET_NR_shmctl 210 +#define TARGET_NR_shmdt 211 +#define TARGET_NR_shmget 212 +#define TARGET_NR_osf_mvalid 213 /* not implemented */ +#define TARGET_NR_osf_getaddressconf 214 /* not implemented */ +#define TARGET_NR_osf_msleep 215 /* not implemented */ +#define TARGET_NR_osf_mwakeup 216 /* not implemented */ +#define TARGET_NR_msync 217 +#define TARGET_NR_osf_signal 218 /* not implemented */ +#define TARGET_NR_osf_utc_gettime 219 /* not implemented */ +#define TARGET_NR_osf_utc_adjtime 220 /* not implemented */ + +#define TARGET_NR_osf_security 222 /* not implemented */ +#define TARGET_NR_osf_kloadcall 223 /* not implemented */ + +#define TARGET_NR_osf_stat 224 +#define TARGET_NR_osf_lstat 225 +#define TARGET_NR_osf_fstat 226 +#define TARGET_NR_osf_statfs64 227 +#define TARGET_NR_osf_fstatfs64 228 + +#define TARGET_NR_getpgid 233 +#define TARGET_NR_getsid 234 +#define TARGET_NR_sigaltstack 235 +#define TARGET_NR_osf_waitid 236 /* not implemented */ +#define TARGET_NR_osf_priocntlset 237 /* not implemented */ +#define TARGET_NR_osf_sigsendset 238 /* not implemented */ +#define TARGET_NR_osf_set_speculative 239 /* not implemented */ +#define TARGET_NR_osf_msfs_syscall 240 /* not implemented */ +#define TARGET_NR_osf_sysinfo 241 +#define TARGET_NR_osf_uadmin 242 /* not implemented */ +#define TARGET_NR_osf_fuser 243 /* not implemented */ +#define TARGET_NR_osf_proplist_syscall 244 +#define TARGET_NR_osf_ntp_adjtime 245 /* not implemented */ +#define TARGET_NR_osf_ntp_gettime 246 /* not implemented */ +#define TARGET_NR_osf_pathconf 247 /* not implemented */ +#define TARGET_NR_osf_fpathconf 248 /* not implemented */ + +#define TARGET_NR_osf_uswitch 250 /* not implemented */ +#define TARGET_NR_osf_usleep_thread 251 +#define TARGET_NR_osf_audcntl 252 /* not implemented */ +#define TARGET_NR_osf_audgen 253 /* not implemented */ +#define TARGET_NR_sysfs 254 +#define TARGET_NR_osf_subsys_info 255 /* not implemented */ +#define TARGET_NR_osf_getsysinfo 256 +#define TARGET_NR_osf_setsysinfo 257 +#define TARGET_NR_osf_afs_syscall 258 /* not implemented */ +#define TARGET_NR_osf_swapctl 259 /* not implemented */ +#define TARGET_NR_osf_memcntl 260 /* not implemented */ +#define TARGET_NR_osf_fdatasync 261 /* not implemented */ + +/* + * Ignore legacy syscalls that we don't use. + */ +#define TARGET_IGNORE_alarm +#define TARGET_IGNORE_creat +#define TARGET_IGNORE_getegid +#define TARGET_IGNORE_geteuid +#define TARGET_IGNORE_getgid +#define TARGET_IGNORE_getpid +#define TARGET_IGNORE_getppid +#define TARGET_IGNORE_getuid +#define TARGET_IGNORE_pause +#define TARGET_IGNORE_time +#define TARGET_IGNORE_utime +#define TARGET_IGNORE_umount2 + +/* + * Linux-specific system calls begin at 300 + */ +#define TARGET_NR_bdflush 300 +#define TARGET_NR_sethae 301 +#define TARGET_NR_mount 302 +#define TARGET_NR_old_adjtimex 303 +#define TARGET_NR_swapoff 304 +#define TARGET_NR_getdents 305 +#define TARGET_NR_create_module 306 +#define TARGET_NR_init_module 307 +#define TARGET_NR_delete_module 308 +#define TARGET_NR_get_kernel_syms 309 +#define TARGET_NR_syslog 310 +#define TARGET_NR_reboot 311 +#define TARGET_NR_clone 312 +#define TARGET_NR_uselib 313 +#define TARGET_NR_mlock 314 +#define TARGET_NR_munlock 315 +#define TARGET_NR_mlockall 316 +#define TARGET_NR_munlockall 317 +#define TARGET_NR_sysinfo 318 +#define TARGET_NR__sysctl 319 +/* 320 was sysTARGETidle. */ +#define TARGET_NR_oldumount 321 +#define TARGET_NR_swapon 322 +#define TARGET_NR_times 323 +#define TARGET_NR_personality 324 +#define TARGET_NR_setfsuid 325 +#define TARGET_NR_setfsgid 326 +#define TARGET_NR_ustat 327 +#define TARGET_NR_statfs 328 +#define TARGET_NR_fstatfs 329 +#define TARGET_NR_sched_setparam 330 +#define TARGET_NR_sched_getparam 331 +#define TARGET_NR_sched_setscheduler 332 +#define TARGET_NR_sched_getscheduler 333 +#define TARGET_NR_sched_yield 334 +#define TARGET_NR_sched_get_priority_max 335 +#define TARGET_NR_sched_get_priority_min 336 +#define TARGET_NR_sched_rr_get_interval 337 +#define TARGET_NR_afs_syscall 338 +#define TARGET_NR_uname 339 +#define TARGET_NR_nanosleep 340 +#define TARGET_NR_mremap 341 +#define TARGET_NR_nfsservctl 342 +#define TARGET_NR_setresuid 343 +#define TARGET_NR_getresuid 344 +#define TARGET_NR_pciconfig_read 345 +#define TARGET_NR_pciconfig_write 346 +#define TARGET_NR_query_module 347 +#define TARGET_NR_prctl 348 +#define TARGET_NR_pread64 349 +#define TARGET_NR_pwrite64 350 +#define TARGET_NR_rt_sigreturn 351 +#define TARGET_NR_rt_sigaction 352 +#define TARGET_NR_rt_sigprocmask 353 +#define TARGET_NR_rt_sigpending 354 +#define TARGET_NR_rt_sigtimedwait 355 +#define TARGET_NR_rt_sigqueueinfo 356 +#define TARGET_NR_rt_sigsuspend 357 +#define TARGET_NR_select 358 +#define TARGET_NR_gettimeofday 359 +#define TARGET_NR_settimeofday 360 +#define TARGET_NR_getitimer 361 +#define TARGET_NR_setitimer 362 +#define TARGET_NR_utimes 363 +#define TARGET_NR_getrusage 364 +#define TARGET_NR_wait4 365 +#define TARGET_NR_adjtimex 366 +#define TARGET_NR_getcwd 367 +#define TARGET_NR_capget 368 +#define TARGET_NR_capset 369 +#define TARGET_NR_sendfile 370 +#define TARGET_NR_setresgid 371 +#define TARGET_NR_getresgid 372 +#define TARGET_NR_dipc 373 +#define TARGET_NR_pivot_root 374 +#define TARGET_NR_mincore 375 +#define TARGET_NR_pciconfig_iobase 376 +#define TARGET_NR_getdents64 377 +#define TARGET_NR_gettid 378 +#define TARGET_NR_readahead 379 +/* 380 is unused */ +#define TARGET_NR_tkill 381 +#define TARGET_NR_setxattr 382 +#define TARGET_NR_lsetxattr 383 +#define TARGET_NR_fsetxattr 384 +#define TARGET_NR_getxattr 385 +#define TARGET_NR_lgetxattr 386 +#define TARGET_NR_fgetxattr 387 +#define TARGET_NR_listxattr 388 +#define TARGET_NR_llistxattr 389 +#define TARGET_NR_flistxattr 390 +#define TARGET_NR_removexattr 391 +#define TARGET_NR_lremovexattr 392 +#define TARGET_NR_fremovexattr 393 +#define TARGET_NR_futex 394 +#define TARGET_NR_sched_setaffinity 395 +#define TARGET_NR_sched_getaffinity 396 +#define TARGET_NR_tuxcall 397 +#define TARGET_NR_io_setup 398 +#define TARGET_NR_io_destroy 399 +#define TARGET_NR_io_getevents 400 +#define TARGET_NR_io_submit 401 +#define TARGET_NR_io_cancel 402 +#define TARGET_NR_exit_group 405 +#define TARGET_NR_lookup_dcookie 406 +#define TARGET_NR_epoll_create 407 +#define TARGET_NR_epoll_ctl 408 +#define TARGET_NR_epoll_wait 409 +/* Feb 2007: These three sysTARGETepoll defines shouldn't be here but culling + * them would break userspace apps ... we'll kill them off in 2010 :) */ +#define TARGET_NR_sys_epoll_create TARGET_NR_epoll_create +#define TARGET_NR_sys_epoll_ctl TARGET_NR_epoll_ctl +#define TARGET_NR_sys_epoll_wait TARGET_NR_epoll_wait +#define TARGET_NR_remap_file_pages 410 +#define TARGET_NR_set_tid_address 411 +#define TARGET_NR_restart_syscall 412 +#define TARGET_NR_fadvise64 413 +#define TARGET_NR_timer_create 414 +#define TARGET_NR_timer_settime 415 +#define TARGET_NR_timer_gettime 416 +#define TARGET_NR_timer_getoverrun 417 +#define TARGET_NR_timer_delete 418 +#define TARGET_NR_clock_settime 419 +#define TARGET_NR_clock_gettime 420 +#define TARGET_NR_clock_getres 421 +#define TARGET_NR_clock_nanosleep 422 +#define TARGET_NR_semtimedop 423 +#define TARGET_NR_tgkill 424 +#define TARGET_NR_stat64 425 +#define TARGET_NR_lstat64 426 +#define TARGET_NR_fstat64 427 +#define TARGET_NR_vserver 428 +#define TARGET_NR_mbind 429 +#define TARGET_NR_get_mempolicy 430 +#define TARGET_NR_set_mempolicy 431 +#define TARGET_NR_mq_open 432 +#define TARGET_NR_mq_unlink 433 +#define TARGET_NR_mq_timedsend 434 +#define TARGET_NR_mq_timedreceive 435 +#define TARGET_NR_mq_notify 436 +#define TARGET_NR_mq_getsetattr 437 +#define TARGET_NR_waitid 438 +#define TARGET_NR_add_key 439 +#define TARGET_NR_request_key 440 +#define TARGET_NR_keyctl 441 +#define TARGET_NR_ioprio_set 442 +#define TARGET_NR_ioprio_get 443 +#define TARGET_NR_inotify_init 444 +#define TARGET_NR_inotify_add_watch 445 +#define TARGET_NR_inotify_rm_watch 446 +#define TARGET_NR_fdatasync 447 +#define TARGET_NR_kexec_load 448 +#define TARGET_NR_migrate_pages 449 +#define TARGET_NR_openat 450 +#define TARGET_NR_mkdirat 451 +#define TARGET_NR_mknodat 452 +#define TARGET_NR_fchownat 453 +#define TARGET_NR_futimesat 454 +#define TARGET_NR_fstatat64 455 +#define TARGET_NR_unlinkat 456 +#define TARGET_NR_renameat 457 +#define TARGET_NR_linkat 458 +#define TARGET_NR_symlinkat 459 +#define TARGET_NR_readlinkat 460 +#define TARGET_NR_fchmodat 461 +#define TARGET_NR_faccessat 462 +#define TARGET_NR_pselect6 463 +#define TARGET_NR_ppoll 464 +#define TARGET_NR_unshare 465 +#define TARGET_NR_set_robust_list 466 +#define TARGET_NR_get_robust_list 467 +#define TARGET_NR_splice 468 +#define TARGET_NR_sync_file_range 469 +#define TARGET_NR_tee 470 +#define TARGET_NR_vmsplice 471 +#define TARGET_NR_move_pages 472 +#define TARGET_NR_getcpu 473 +#define TARGET_NR_epoll_pwait 474 +#define TARGET_NR_utimensat 475 +#define TARGET_NR_signalfd 476 +#define TARGET_NR_timerfd 477 +#define TARGET_NR_eventfd 478 +#define TARGET_NR_recvmmsg 479 +#define TARGET_NR_fallocate 480 +#define TARGET_NR_timerfd_create 481 +#define TARGET_NR_timerfd_settime 482 +#define TARGET_NR_timerfd_gettime 483 +#define TARGET_NR_signalfd4 484 +#define TARGET_NR_eventfd2 485 +#define TARGET_NR_epoll_create1 486 +#define TARGET_NR_dup3 487 +#define TARGET_NR_pipe2 488 +#define TARGET_NR_inotify_init1 489 +#define TARGET_NR_preadv 490 +#define TARGET_NR_pwritev 491 +#define TARGET_NR_rt_tgsigqueueinfo 492 +#define TARGET_NR_perf_event_open 493 +#define TARGET_NR_fanotify_init 494 +#define TARGET_NR_fanotify_mark 495 +#define TARGET_NR_prlimit64 496 +#define TARGET_NR_name_to_handle_at 497 +#define TARGET_NR_open_by_handle_at 498 +#define TARGET_NR_clock_adjtime 499 +#define TARGET_NR_syncfs 500 +#define TARGET_NR_setns 501 +#define TARGET_NR_accept4 502 +#define TARGET_NR_sendmmsg 503 +#define TARGET_NR_process_vm_readv 504 +#define TARGET_NR_process_vm_writev 505 +#define TARGET_NR_sw_slave_rwperfmons 506 +#define TARGET_NR_sys_get_vmflags 507 diff --git a/linux-user/sw64/target_cpu.h b/linux-user/sw64/target_cpu.h new file mode 100644 index 0000000000..1b87c8ba6d --- /dev/null +++ b/linux-user/sw64/target_cpu.h @@ -0,0 +1,38 @@ +/* + * SW64 specific CPU ABI and functions for linux-user + * + * 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 . + */ +#ifndef SW64_TARGET_CPU_H +#define SW64_TARGET_CPU_H + +static inline void cpu_clone_regs(CPUSW64State *env, target_ulong newsp) +{ + if (newsp) { + env->ir[IDX_SP] = newsp; + } + env->ir[IDX_V0] = 0; + env->ir[IDX_A3] = 0; +} + +static inline void cpu_set_tls(CPUSW64State *env, target_ulong newtls) +{ + env->unique = newtls; +} + +static inline abi_ulong get_sp_from_cpustate(CPUSW64State *state) +{ + return state->ir[IDX_SP]; +} +#endif diff --git a/linux-user/sw64/target_elf.h b/linux-user/sw64/target_elf.h new file mode 100644 index 0000000000..be48b6dee3 --- /dev/null +++ b/linux-user/sw64/target_elf.h @@ -0,0 +1,14 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SW64_TARGET_ELF_H +#define SW64_TARGET_ELF_H +static inline const char *cpu_get_model(uint32_t eflags) +{ + return "any"; +} +#endif diff --git a/linux-user/sw64/target_fcntl.h b/linux-user/sw64/target_fcntl.h new file mode 100644 index 0000000000..9721e3de39 --- /dev/null +++ b/linux-user/sw64/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SW64_TARGET_FCNTL_H +#define sw64_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/sw64/target_signal.h b/linux-user/sw64/target_signal.h new file mode 100644 index 0000000000..6393a7542f --- /dev/null +++ b/linux-user/sw64/target_signal.h @@ -0,0 +1,98 @@ +#ifndef SW64_TARGET_SIGNAL_H +#define SW64_TARGET_SIGNAL_H + +#include "cpu.h" + +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGSTKFLT 7 /* actually SIGEMT */ +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGURG 16 +#define TARGET_SIGSTOP 17 +#define TARGET_SIGTSTP 18 +#define TARGET_SIGCONT 19 +#define TARGET_SIGCHLD 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGIO 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGPWR 29 /* actually SIGINFO */ +#define TARGET_SIGUSR1 30 +#define TARGET_SIGUSR2 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 +#define TARGET_SIG_UNBLOCK 2 +#define TARGET_SIG_SETMASK 3 + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + int32_t ss_flags; + int32_t dummy; + abi_ulong ss_size; +} target_stack_t; + + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_SA_ONSTACK 0x00000001 +#define TARGET_SA_RESTART 0x00000002 +#define TARGET_SA_NOCLDSTOP 0x00000004 +#define TARGET_SA_NODEFER 0x00000008 +#define TARGET_SA_RESETHAND 0x00000010 +#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000040 + +#define TARGET_MINSIGSTKSZ 4096 +#define TARGET_SIGSTKSZ 16384 + +/* From . */ +#define TARGET_GEN_INTOVF -1 /* integer overflow */ +#define TARGET_GEN_INTDIV -2 /* integer division by zero */ +#define TARGET_GEN_FLTOVF -3 /* fp overflow */ +#define TARGET_GEN_FLTDIV -4 /* fp division by zero */ +#define TARGET_GEN_FLTUND -5 /* fp underflow */ +#define TARGET_GEN_FLTINV -6 /* invalid fp operand */ +#define TARGET_GEN_FLTINE -7 /* inexact fp operand */ +#define TARGET_GEN_DECOVF -8 /* decimal overflow (for COBOL??) */ +#define TARGET_GEN_DECDIV -9 /* decimal division by zero */ +#define TARGET_GEN_DECINV -10 /* invalid decimal operand */ +#define TARGET_GEN_ROPRAND -11 /* reserved operand */ +#define TARGET_GEN_ASSERTERR -12 /* assertion error */ +#define TARGET_GEN_NULPTRERR -13 /* null pointer error */ +#define TARGET_GEN_STKOVF -14 /* stack overflow */ +#define TARGET_GEN_STRLENERR -15 /* string length error */ +#define TARGET_GEN_SUBSTRERR -16 /* substring error */ +#define TARGET_GEN_RANGERR -17 /* range error */ +#define TARGET_GEN_SUBRNG -18 +#define TARGET_GEN_SUBRNG1 -19 +#define TARGET_GEN_SUBRNG2 -20 +#define TARGET_GEN_SUBRNG3 -21 +#define TARGET_GEN_SUBRNG4 -22 +#define TARGET_GEN_SUBRNG5 -23 +#define TARGET_GEN_SUBRNG6 -24 +#define TARGET_GEN_SUBRNG7 -25 + +#define TARGET_ARCH_HAS_SETUP_FRAME +#endif /* SW64_TARGET_SIGNAL_H */ diff --git a/linux-user/sw64/target_structs.h b/linux-user/sw64/target_structs.h new file mode 100644 index 0000000000..7c13dc4bac --- /dev/null +++ b/linux-user/sw64/target_structs.h @@ -0,0 +1,47 @@ +/* + * SW64 specific structures for linux-user + * + * Copyright (c) 2018 Lin Hainan + * + * 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. + * + */ +#ifndef SW64_TARGET_STRUCTS_H +#define SW64_TARGET_STRUCTS_H + +/* TODO: Maybe it should be update. now it's different from other arch */ +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad1; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/sw64/target_syscall.h b/linux-user/sw64/target_syscall.h new file mode 100644 index 0000000000..c901ae95d8 --- /dev/null +++ b/linux-user/sw64/target_syscall.h @@ -0,0 +1,121 @@ +#ifndef SW64_TARGET_SYSCALL_H +#define SW64_TARGET_SYSCALL_H + +/* TODO */ +struct target_pt_regs { + abi_ulong r0; + abi_ulong r1; + abi_ulong r2; + abi_ulong r3; + abi_ulong r4; + abi_ulong r5; + abi_ulong r6; + abi_ulong r7; + abi_ulong r8; + abi_ulong r19; + abi_ulong r20; + abi_ulong r21; + abi_ulong r22; + abi_ulong r23; + abi_ulong r24; + abi_ulong r25; + abi_ulong r26; + abi_ulong r27; + abi_ulong r28; + abi_ulong hae; +/* JRP - These are the values provided to a0-a2 by HMcode */ + abi_ulong trap_a0; + abi_ulong trap_a1; + abi_ulong trap_a2; +/* These are saved by HMcode: */ + abi_ulong ps; + abi_ulong pc; + abi_ulong gp; + abi_ulong r16; + abi_ulong r17; + abi_ulong r18; +}; + +#define TARGET_MLOCKALL_MCL_CURRENT 0x2000 +#define TARGET_MLOCKALL_MCL_FUTURE 0x4000 + + +#define UNAME_MACHINE "sw64" +#define UNAME_MINIMUM_RELEASE "2.6.32" +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 45 /* Operation not supported on transport endpoint */ +#define SWCR_STATUS_INV0 (1UL<<17) +#define SWCR_STATUS_DZE0 (1UL<<18) +#define SWCR_STATUS_OVF0 (1UL<<19) +#define SWCR_STATUS_UNF0 (1UL<<20) +#define SWCR_STATUS_INE0 (1UL<<21) +#define SWCR_STATUS_DNO0 (1UL<<22) + +#define SWCR_STATUS_MASK0 (SWCR_STATUS_INV0 | SWCR_STATUS_DZE0 | \ + SWCR_STATUS_OVF0 | SWCR_STATUS_UNF0 | \ + SWCR_STATUS_INE0 | SWCR_STATUS_DNO0) + +#define SWCR_STATUS0_TO_EXCSUM_SHIFT 16 + +#define SWCR_STATUS_INV1 (1UL<<23) +#define SWCR_STATUS_DZE1 (1UL<<24) +#define SWCR_STATUS_OVF1 (1UL<<25) +#define SWCR_STATUS_UNF1 (1UL<<26) +#define SWCR_STATUS_INE1 (1UL<<27) +#define SWCR_STATUS_DNO1 (1UL<<28) + +#define SWCR_STATUS_MASK1 (SWCR_STATUS_INV1 | SWCR_STATUS_DZE1 | \ + SWCR_STATUS_OVF1 | SWCR_STATUS_UNF1 | \ + SWCR_STATUS_INE1 | SWCR_STATUS_DNO1) + +#define SWCR_STATUS1_TO_EXCSUM_SHIFT 22 +#define SWCR_STATUS_INV2 (1UL<<34) +#define SWCR_STATUS_DZE2 (1UL<<35) +#define SWCR_STATUS_OVF2 (1UL<<36) +#define SWCR_STATUS_UNF2 (1UL<<37) +#define SWCR_STATUS_INE2 (1UL<<38) +#define SWCR_STATUS_DNO2 (1UL<<39) + +#define SWCR_STATUS_MASK2 (SWCR_STATUS_INV2 | SWCR_STATUS_DZE2 | \ + SWCR_STATUS_OVF2 | SWCR_STATUS_UNF2 | \ + SWCR_STATUS_INE2 | SWCR_STATUS_DNO2) +#define SWCR_STATUS_INV3 (1UL<<40) +#define SWCR_STATUS_DZE3 (1UL<<41) +#define SWCR_STATUS_OVF3 (1UL<<42) +#define SWCR_STATUS_UNF3 (1UL<<43) +#define SWCR_STATUS_INE3 (1UL<<44) +#define SWCR_STATUS_DNO3 (1UL<<45) + +#define SWCR_STATUS_MASK3 (SWCR_STATUS_INV3 | SWCR_STATUS_DZE3 | \ + SWCR_STATUS_OVF3 | SWCR_STATUS_UNF3 | \ + SWCR_STATUS_INE3 | SWCR_STATUS_DNO3) +#define SWCR_TRAP_ENABLE_INV (1UL<<1) /* invalid op */ +#define SWCR_TRAP_ENABLE_DZE (1UL<<2) /* division by zero */ +#define SWCR_TRAP_ENABLE_OVF (1UL<<3) /* overflow */ +#define SWCR_TRAP_ENABLE_UNF (1UL<<4) /* underflow */ +#define SWCR_TRAP_ENABLE_INE (1UL<<5) /* inexact */ +#define SWCR_TRAP_ENABLE_DNO (1UL<<6) /* denorm */ +#define SWCR_TRAP_ENABLE_MASK (SWCR_TRAP_ENABLE_INV | SWCR_TRAP_ENABLE_DZE | \ + SWCR_TRAP_ENABLE_OVF | SWCR_TRAP_ENABLE_UNF | \ + SWCR_TRAP_ENABLE_INE | SWCR_TRAP_ENABLE_DNO) + +/* Denorm and Underflow flushing */ +#define SWCR_MAP_DMZ (1UL<<12) /* Map denorm inputs to zero */ +#define SWCR_MAP_UMZ (1UL<<13) /* Map underflowed outputs to zero */ + +#define SWCR_MAP_MASK (SWCR_MAP_DMZ | SWCR_MAP_UMZ) + +/* status bits coming from fpcr: */ +#define SWCR_STATUS_INV (1UL<<17) +#define SWCR_STATUS_DZE (1UL<<18) +#define SWCR_STATUS_OVF (1UL<<19) +#define SWCR_STATUS_UNF (1UL<<20) +#define SWCR_STATUS_INE (1UL<<21) +#define SWCR_STATUS_DNO (1UL<<22) + +#define SWCR_STATUS_MASK (SWCR_STATUS_INV | SWCR_STATUS_DZE | \ + SWCR_STATUS_OVF | SWCR_STATUS_UNF | \ + SWCR_STATUS_INE | SWCR_STATUS_DNO) +#define TARGET_GSI_IEEE_FP_CONTROL 45 +#define TARGET_SSI_IEEE_FP_CONTROL 14 +#endif diff --git a/linux-user/sw64/termbits.h b/linux-user/sw64/termbits.h new file mode 100644 index 0000000000..37dd77120c --- /dev/null +++ b/linux-user/sw64/termbits.h @@ -0,0 +1,265 @@ +typedef unsigned char target_cc_t; +typedef unsigned int target_speed_t; +typedef unsigned int target_tcflag_t; + +#define TARGET_NCCS 19 +struct target_termios { + target_tcflag_t c_iflag; /* input mode flags */ + target_tcflag_t c_oflag; /* output mode flags */ + target_tcflag_t c_cflag; /* control mode flags */ + target_tcflag_t c_lflag; /* local mode flags */ + target_cc_t c_cc[TARGET_NCCS]; /* control characters */ + target_cc_t c_line; /* line discipline (== c_cc[19]) */ + target_speed_t c_ispeed; /* input speed */ + target_speed_t c_ospeed; /* output speed */ +}; + +/* c_cc characters */ +#define TARGET_VEOF 0 +#define TARGET_VEOL 1 +#define TARGET_VEOL2 2 +#define TARGET_VERASE 3 +#define TARGET_VWERASE 4 +#define TARGET_VKILL 5 +#define TARGET_VREPRINT 6 +#define TARGET_VSWTC 7 +#define TARGET_VINTR 8 +#define TARGET_VQUIT 9 +#define TARGET_VSUSP 10 +#define TARGET_VSTART 12 +#define TARGET_VSTOP 13 +#define TARGET_VLNEXT 14 +#define TARGET_VDISCARD 15 +#define TARGET_VMIN 16 +#define TARGET_VTIME 17 + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IXON 0001000 +#define TARGET_IXOFF 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IUCLC 0010000 +#define TARGET_IMAXBEL 0020000 +#define TARGET_IUTF8 0040000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_ONLCR 0000002 +#define TARGET_OLCUC 0000004 + +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 + +#define TARGET_OFILL 00000100 +#define TARGET_OFDEL 00000200 +#define TARGET_NLDLY 00001400 +#define TARGET_NL0 00000000 +#define TARGET_NL1 00000400 +#define TARGET_NL2 00001000 +#define TARGET_NL3 00001400 +#define TARGET_TABDLY 00006000 +#define TARGET_TAB0 00000000 +#define TARGET_TAB1 00002000 +#define TARGET_TAB2 00004000 +#define TARGET_TAB3 00006000 +#define TARGET_CRDLY 00030000 +#define TARGET_CR0 00000000 +#define TARGET_CR1 00010000 +#define TARGET_CR2 00020000 +#define TARGET_CR3 00030000 +#define TARGET_FFDLY 00040000 +#define TARGET_FF0 00000000 +#define TARGET_FF1 00040000 +#define TARGET_BSDLY 00100000 +#define TARGET_BS0 00000000 +#define TARGET_BS1 00100000 +#define TARGET_VTDLY 00200000 +#define TARGET_VT0 00000000 +#define TARGET_VT1 00200000 +#define TARGET_XTABS 01000000 /* Hmm.. Linux/i386 considers this part of TABDLY.. */ + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0000037 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA B19200 +#define TARGET_EXTB B38400 +#define TARGET_CBAUDEX 0000000 +#define TARGET_B57600 00020 +#define TARGET_B115200 00021 +#define TARGET_B230400 00022 +#define TARGET_B460800 00023 +#define TARGET_B500000 00024 +#define TARGET_B576000 00025 +#define TARGET_B921600 00026 +#define TARGET_B1000000 00027 +#define TARGET_B1152000 00030 +#define TARGET_B1500000 00031 +#define TARGET_B2000000 00032 +#define TARGET_B2500000 00033 +#define TARGET_B3000000 00034 +#define TARGET_B3500000 00035 +#define TARGET_B4000000 00036 + +#define TARGET_CSIZE 00001400 +#define TARGET_CS5 00000000 +#define TARGET_CS6 00000400 +#define TARGET_CS7 00001000 +#define TARGET_CS8 00001400 + +#define TARGET_CSTOPB 00002000 +#define TARGET_CREAD 00004000 +#define TARGET_PARENB 00010000 +#define TARGET_PARODD 00020000 +#define TARGET_HUPCL 00040000 + +#define TARGET_CLOCAL 00100000 +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define TARGET_ISIG 0x00000080 +#define TARGET_ICANON 0x00000100 +#define TARGET_XCASE 0x00004000 +#define TARGET_ECHO 0x00000008 +#define TARGET_ECHOE 0x00000002 +#define TARGET_ECHOK 0x00000004 +#define TARGET_ECHONL 0x00000010 +#define TARGET_NOFLSH 0x80000000 +#define TARGET_TOSTOP 0x00400000 +#define TARGET_ECHOCTL 0x00000040 +#define TARGET_ECHOPRT 0x00000020 +#define TARGET_ECHOKE 0x00000001 +#define TARGET_FLUSHO 0x00800000 +#define TARGET_PENDIN 0x20000000 +#define TARGET_IEXTEN 0x00000400 + +#define TARGET_FIOCLEX TARGET_IO('f', 1) +#define TARGET_FIONCLEX TARGET_IO('f', 2) +#define TARGET_FIOASYNC TARGET_IOW('f', 125, int) +#define TARGET_FIONBIO TARGET_IOW('f', 126, int) +#define TARGET_FIONREAD TARGET_IOR('f', 127, int) +#define TARGET_TIOCINQ FIONREAD +#define TARGET_FIOQSIZE TARGET_IOR('f', 128, loff_t) + +#define TARGET_TIOCGETP TARGET_IOR('t', 8, struct target_sgttyb) +#define TARGET_TIOCSETP TARGET_IOW('t', 9, struct target_sgttyb) +#define TARGET_TIOCSETN TARGET_IOW('t', 10, struct target_sgttyb) /* TIOCSETP wo flush */ + +#define TARGET_TIOCSETC TARGET_IOW('t', 17, struct target_tchars) +#define TARGET_TIOCGETC TARGET_IOR('t', 18, struct target_tchars) +#define TARGET_TCGETS TARGET_IOR('t', 19, struct target_termios) +#define TARGET_TCSETS TARGET_IOW('t', 20, struct target_termios) +#define TARGET_TCSETSW TARGET_IOW('t', 21, struct target_termios) +#define TARGET_TCSETSF TARGET_IOW('t', 22, struct target_termios) + +#define TARGET_TCGETA TARGET_IOR('t', 23, struct target_termio) +#define TARGET_TCSETA TARGET_IOW('t', 24, struct target_termio) +#define TARGET_TCSETAW TARGET_IOW('t', 25, struct target_termio) +#define TARGET_TCSETAF TARGET_IOW('t', 28, struct target_termio) + +#define TARGET_TCSBRK TARGET_IO('t', 29) +#define TARGET_TCXONC TARGET_IO('t', 30) +#define TARGET_TCFLSH TARGET_IO('t', 31) + +#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct target_winsize) +#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct target_winsize) +#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */ +#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */ +#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */ + +#define TARGET_TIOCGLTC TARGET_IOR('t', 116, struct target_ltchars) +#define TARGET_TIOCSLTC TARGET_IOW('t', 117, struct target_ltchars) +#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) +#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) + +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E + +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +# define TARGET_TIOCM_LE 0x001 +# define TARGET_TIOCM_DTR 0x002 +# define TARGET_TIOCM_RTS 0x004 +# define TARGET_TIOCM_ST 0x008 +# define TARGET_TIOCM_SR 0x010 +# define TARGET_TIOCM_CTS 0x020 +# define TARGET_TIOCM_CAR 0x040 +# define TARGET_TIOCM_RNG 0x080 +# define TARGET_TIOCM_DSR 0x100 +# define TARGET_TIOCM_CD TIOCM_CAR +# define TARGET_TIOCM_RI TIOCM_RNG +# define TARGET_TIOCM_OUT1 0x2000 +# define TARGET_TIOCM_OUT2 0x4000 +# define TARGET_TIOCM_LOOP 0x8000 + +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +# define TARGET_TIOCPKT_DATA 0 +# define TARGET_TIOCPKT_FLUSHREAD 1 +# define TARGET_TIOCPKT_FLUSHWRITE 2 +# define TARGET_TIOCPKT_STOP 4 +# define TARGET_TIOCPKT_START 8 +# define TARGET_TIOCPKT_NOSTOP 16 +# define TARGET_TIOCPKT_DOSTOP 32 + + +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ +#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ +#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TARGET_TIOCGPTN TARGET_IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TARGET_TIOCSPTLCK TARGET_IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TARGET_TIOCGPTPEER TARGET_IO('T', 0x41) /* Safely open the slave */ + +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ + /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ +# define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ +#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ diff --git a/meson.build b/meson.build index 96de1a6ef9..d0bbceffe1 100644 --- a/meson.build +++ b/meson.build @@ -56,7 +56,7 @@ python = import('python').find_installation() supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64', - 'arm', 'aarch64', 'mips', 'mips64', 'sparc', 'sparc64'] + 'arm', 'aarch64', 'mips', 'mips64', 'sparc', 'sparc64', 'sw64'] cpu = host_machine.cpu_family() @@ -65,6 +65,10 @@ if cpu in ['riscv32', 'riscv64'] cpu = 'riscv' endif +if cpu == 'sw_64' + cpu = 'sw64' +endif + targetos = host_machine.system() if cpu in ['x86', 'x86_64'] @@ -77,6 +81,8 @@ elif cpu in ['ppc', 'ppc64'] kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] elif cpu in ['mips', 'mips64'] kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] +elif cpu == 'sw64' + kvm_targets = ['sw64-softmmu'] else kvm_targets = [] endif @@ -359,6 +365,8 @@ if not get_option('tcg').disabled() tcg_arch = 'i386' elif config_host['ARCH'] == 'ppc64' tcg_arch = 'ppc' + elif config_host['ARCH'] in ['sw64'] + tcg_arch = 'sw64' endif add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch, language: ['c', 'cpp', 'objc']) @@ -1814,6 +1822,7 @@ disassemblers = { 'sh4' : ['CONFIG_SH4_DIS'], 'sparc' : ['CONFIG_SPARC_DIS'], 'xtensa' : ['CONFIG_XTENSA_DIS'], + 'sw64' : ['CONFIG_SW64_DIS'], } if link_language == 'cpp' disassemblers += { @@ -2466,6 +2475,7 @@ if have_system 'hw/sparc', 'hw/sparc64', 'hw/ssi', + 'hw/sw64', 'hw/timer', 'hw/tpm', 'hw/usb', diff --git a/pc-bios/core3-hmcode b/pc-bios/core3-hmcode new file mode 100755 index 0000000000000000000000000000000000000000..6092ad0d880092b6be4603015911ca2b91596845 GIT binary patch literal 225904 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%0D8ZXKbZKLW3iM{)MV zpB-eo7!`BtfQaFNZ*6$NaX>_im~X3(9kR|zs28svP3!aw)!YuB>YL$Ht`OV^X&c-I zpF+WV%Ab~x`m;VT3LhBN6=-HaI<5Z0L8EMasw?K8Ps*rMMgOn83hA@aYJPkQ#M8^ckk)(GxBShT_o4PF_i;wMFa+jcAKd)QGkilvT51#*fVIB+WRR6_m_o1qF@0rl}b_ zF2D_)270zBr&**MQe*t9O}7loGhAbN0Zrw#MN`WQXli)@&5+HNZiH;Dk0DZn$wxGr z&n{ut`oLd;TR!lY0)k`4)2LSFd&iz{>t_W7{j7kXv6;S@v6&o0M9xLQK-OP!FZ;u? z;8j>wK+y2YFJXAaF+{SnyWFNm<92`05Sj*;ls1&fI_2Vkii-mxZn(1tQC0;!AOjxIk>?EW(9Vje;WF-j88A@1MtjER?=VFJ zE+`tJ;Ge{F1)?DeL<0))^cdU*MFR?ohA4#jpiZwL3SmBsZIDwWL)`E(0yqp)@E}8dfA+Sn$aQoEx_oHI1Y$R zk`V|t0v89Iaj_;s6JO z`W=mOg7Q9hL<7uBfRo4|8S7dm10| z;=G@MWk~J3m+>vR^Ingkb=S(+5Wmi!6;YIrjKMTCF7+f<7LTHzqw>fyOfuA zsixq~uk_E1QbD7df=0Oly`z!sRkH#L^70A>i%$+KPB2)UATJ(>faj@yyk?yWkAODc zHEXHOcQqKVU7PQkwbSOi8jP1EZw3ap+@*ZeUCI?w+Vk3MRSNR*3i9HU!ip=%iz~>B zj}9xYATO>UFFrA>xPrX6g1mTYvVWV+-*yRkab?MiPYNrpATO>UFFrb~xPrX6g1q>| zu;L2x;tKNO)EfLL9NCR`DHm`lSJ0;lX9e`B!Y1U!Npx^%(08veW9xDcWO&@CI zeaMQlE>qgE9@9gtAUSUKm#D(*EPSXHSI-QzSsf}ua!4YX=5*GMm!-N{Pb*8il$UoY zSJ3Z&W~*0_m)GxtW-aw63nt{n_2&zCB`TestabD5Yf>)r3hT`C++!7$`nH>tmv?Qs zf^nAd1dOwc0fWW0vy5-4on;Kji;u6Xq?fh)Chr-0oNt52xsG};sXsByM%T~bT7C{! zlQVAQyJg-rO;#+M!L_m(T#ACO9T?u`xs;2zlq>jisW2?ddgJGCB|j&q=G}F3Z!u_M z$Ovj7gG<%djIBzK^{@Q0QrQeHmCfK%{=-|ojeZUn^K-abePtEh;@2J@`<`WUxSC&v ztC`)4w+GGc#iU$8y@g2cxCYykpTqs}a{_7=@}QXM_QeQz$ zZ*ybe?>PPa<~4eCwZFkP0*gjp_AHg_2|&V@|Jg{-(YJ0@gn z^(-c(XEDJ#nxs;#9tVU-nwd=0%w)1vl{Cr3CTnIgSu>N#Ry2&Sqj!EshV2z2kBJ(2 zOw(+}{vOequ>o1}k?AUWSzMbW>NR9PgE`}81f?p?={K5|mlG88a>CTi-Wc0K4$x~u zs-F&1b90zl-GmV|rFMe<1j}>H&tO`929uK8KO#*N0%?%zbw0jl*(@ffmtulr2CBiI z+@O#~%yOA5%Vo;dl@*mKIy{2il$OI3wH&6FJ+*}B8F@_6$m5#X(@U7Bk;zq!OeQ;q zCKok~ud12QaB|qz^K2%~vzfSj?LSO4%b8r3bDiv@Qr3*rCk9jvTq#?EN!5)^rN;~k zb4tr$Vp>j^+CZzCn-iwy=7gx#H%zFZ;X1j<4;kr&mK&m~$V%ivP}2~pcec+8tU z%W7c^WN|@x3N>b9%5GcDh4pi|cYY36<0B7Oi4Q$=>M3NWs2S7h{7W5US{=s$5xt+v zDof~XiU9+~>$I!Gtfhp3;t~dmn^DLLm|c@0Jy`&9SBr(7ZEg+Kp}E4a3v zg1k63u8_09E0+aY5xT8F_jJr!$xg>waUBOlc;&9Kq5%m-0}AH!S83yFnA2YaJfK4t zm+F$k+YwnIFC*v=N(7{2)mk@|RRRwPfqOb$dy(J2q1{vAUM0F~o_D*m@$`3R6JD3O zOU?3bUbwc=sN9e5_?KRAnPVt%49BuqU6HY|Sa>EEG)e|!LoO(MSv@l#t7kGFHTau>RzF{i2n(NHdqova&&Cy*&h#_1vJco*PuAFICyXN?z%? zL1jIcDa$5g*;{%R)6}z=Al==-D<~m*%*W@bP(T{%7#)(UE9|=;oKrIX^4lou3sdXz@>$vN-5xaY3Wk^#e|P#ae|P#aeu3cV4l0A))u0S`69=mb zOLmV*D(Gj1sQQ@}+3=O5Oso~j!(sQ^ zROOpkEAmZj710+bQ|X42wHKJO{eu@g&BRubW@4+Ter$#Rrb(aY}*>Wz?azm80+z@4WrOpAoOZMGT*&*6`=@4;LZ4=kCL&WuLF76Ld16`<=#YMF& zE{Ib>oeXn8(#YhZMrMetJQ>K*hE3VX4-q%=xjr8~yB!xFKI;?;awfJ^rrDtMgXOt@#9dOpxr2%JDXU!F;Hvgtl zR>7rQ!KGYbQpK3C$OCf?U@k(^es8I};dhdZLc#sqG7UmUq{mOFqK|KT%|Dm{SArQ# zs*<$0iegMaF+~^%2tmF)~gsRs~9gkDx|DrysTubEZx!JE=$BpO2i7* z(1aOYQZZIkF;*6g}&Z`7qMjEXdZQuM9z zbfEt=p|?W~OTBml&6Jkkbo6~y0vgsOG^~qMjUQW4GcF*aVO>PSx`?s)$_ZohHKLbN zAFYzNaH1*d8ATL_&NCJc;tPU2BMpRP%!6r{HL;m)$#z!VgA66((zuHSJYSsFKPt z)Qk;sWsJD$$S7!hP0_V-kx{Yn9Yxp5_=uv8jDp7J6J0Bs$K*fG*O5`swK7~aKAGqi zG`^RpBcq`4r9{`t_)MaXjDqr$hyE$WH3Ku81vAQ0`2A6kThv8HNf#LfDc1hD#dA#} zqo8YL6r@=5f-W)&y2vOB3UE0-8;|j1}b6{Q)H~5TObn} z(y7UG(}Sj@`>QmkbZok6{KOGss>QVVXU<7an|sljv!|zLopTAZ0@@lj`Gv|_`iXQwuH+|v!sTa%_Ze3wH*-*%j0m@mz;{54zE=te8VCp^{=%&Yn9x>%XVXoX=u-o~^@lJAku3kE|Xi&YpfAe405&t&!0`bVAlMZsBhjJtj?pbXDd{8 ztZbEf8(mQAndUVgY9-nlHKy#^nNz2wr(HZ%BU8`1`2V$bExnQBMp$_u$R2YGkW+x1 zgO+CWI!g9<#+aAWw%186Lc3eG6}ul%%l1xA`9t{+3+x|DkrZDaS({6Y-D$DvE0QYd zBTE#kJ9Ubi)l0WsP3s3w7K%a?RkU!7rqz9SpzG;~HmgaTog>-y+iojF+CA!I;0)=z zYrjths^9uXmWIu0?wBfiC2^($ovBnAqU<|SHqD8ep)tyiJEfn;+aK1Oem&peb79zZ zbZ6Savni6`h+HTV}qy66d%(u3mc4i|P4DotwI` zKlW2cL(=Q&v41`!$8Sd^GtJAv8eBwQig2EgD{1`NZHFQG@cE>nZByMBv3ZJqfD6jB zdUUn?n;v=|b{l7k z(05=e5@MKE5qbuuB6Eu|1!xX@&Bb@C$4vt$kTB=@nj;iQnDczi5eg*CdA{Zd1rp{w zUvq>433HyWIYNPiInUP|p+Lf%=WC8oAYsn)HAg6rFz5N2BNRxO^L)(_3M9;VzUBx8 z66QQ#bA$p3bDpm`LV<)i&(|EGNJ5^kIY0~GYr*q1fC32%p05Q$frJIm*8-tH!h+{( zflwe}!Sl61D3Gw=`C1?pNLcWEEf5MMEO@>a2n7-rJYNfh0tpMAuLVMZgayyn0-->{ zg6C_2P#|H!^R+-Ikg(wSS|AiiSnzx;5Q-$^`C0(91iqF$UjrzRu;lq#A{0ni@_a23 z3M4FfzLp3D5|%t)ON0UmOP;SKLV<)O&({*6K*EyeYl%=GVafBgL@1E3+y#(p!SgVH0=X7E4;KgpaxHisE-X~8G|-}1!~?U+oZIsBq2CCI^hIf)4WP+_XVe0rK$8W}sD*_tPvsBCM!Kgorh7L_4yZH+ zsC)$!SnzCFSm@1?xf)VoTKIe=mZqZDcUBpw>`V$XSIW;^4)oJk+6D9ZbL`5^D$@$N z>ZFa1LP}$Rv&k--Wsbb`t7vlGJ3wV%0RytKY4#a$zS1<@2v8duP$#c z2=19vmoGVcq4O({61`3)npj?)v({JVU`ztnmU?q?^;gbZY5f{znXZJ~A-=dRa`sA- z&_{Q6{pIB9{OgzQ;_J=3WwKt;dg{Y=-F4IL@N(F7tIcK%rTgb8nE})7lkWE1pj_3| zoly6jrPy4K^(qd9Nh^)sS;`clFlnXy{FWlr(oj{+Gd4viOj>DlxKp(v)J|GT&(Ciu zKr>)<#LLtb^_;@t4(3_?FeWwWXl|~a*t?g&P&WxWc9q7$cyo!r9AztHY z{(lt1;7OFUv&uk~Z-7o2&%6wwK&OmnUS^?}XP>SwMb>SwI;u3Ld)|1w+3zNL<+309 zZrXSM>c@TO&Qdeh8wsg}cT4)=u}Dj^N;jpEp7{s)HYQ>$71~sxJVPjHRAbSlB63+fpbD?1mx; z+RPnyuT&+Q;kXhzE2e>7>n5+G{OlZ;bvGXx`t^m#Js=gPE&6Ft%DIW*l*RxrsjBm6 zg-T<9HvFMp38Fmq<1U$x?S2W37jW;&winSGi^TGsrVLhY4^t;z*muL7dU+}YYR`Zk zgXF+c!Q55x(_8?BxvS!*xynKXUm1VSDoHZ&a#p>|ru_%vjI6+UxjmnXuG909Iz5=_ zDt=O|5DGJ0#ZQWrg`QK|MdX$uvA&$$lF0f#29k14L*VM)d+AXub?Vk4gL-LR3 z?LmxL`*FB8g*)-L_T64v&PxZ>{O}M!A+)XeD7Z!_u(;+4UnA7ASap~ue2q{DZEHRX zt`Q2MZOuo)H9~=zHBaIip}@?VCvlBXU}nvexJD?DxaLV*BNUif^CYej3e2o|64w?= z0mql~zg$OIt99mvr&xytYn=hho2vn$Hat-SC`{Q6KV>%v1x_|RCmVzU(HfqU4MKrv z4bRC2p+K~T=VXIWAX>w7vOy>it>HP@Sm@>7yKDZbNh=dQl6KE`PtV5(xGjKe4bRKQ zLf?ysqlpIVvkF=Pay2{;8-xP68lHy@LM^!z1$Z7d2!$E4iPBQlicpv#>B}i1@tHEU zP+Dqzw>(_7gu*c@>()>GY7Eh@ zV;5<+69AaYHfE)pjw70cePfiS~T|t_|deN;Ty^*R^sOA9Pc+`K;`SkV6d+{_E zy9ipRt5AzJEsR<%Kfnc07`0k{fNK#7qgKlga4kY%)N1(wu0<$}S}i}owFtGNmb~*@ zet>Hc3Xw$12OTX!?TDpXU@8ZzVV#yusiOt&5~ z%omx_j>69uq223lnnti|-Bmh!x}(V$^RkA`kZ=|VgwVCu*$`?aM3;i*wZb)3IT6ax z&5v+eQ)1W1k)9yc&U^9X zm{5;t`~GIXT@Aa0ZYEmgoD0XEy1!YC-@5hTIl14{Gv@8ESJUadV}E|Pb~o$>3Y_Go z{Fkj+K%+yGs2~%L5?A6CR6f$!Prg+6Z}3F|qZk%&QaEVi_`yQhP)#GdqgFB>_VDBoNPr z!YP5yIm=Kf-12i4vtI&8;Ftj65=wfY7e% zivXPa%5PO!nmfx^x^DC}&IIdcYSLNSB+fB*8i5-l-TfbqR8>&&nYsedHS6V{R|_}C z5Zcc%o$u%v!o6Glj#*d7>|9^R^nu%AAlGj(y>XoMxtX;V-7tiic*CFvHk+NNDj0@t zM|p(9JOo%ZRnyUBiPSFg22pG(d*O6W7=gy&srskW$w_thY`E_9@_t8eiuD*^Gj~N4bu4?A42Kr6HYayCsOpKd<;d??Gb&NcGR$lFCtYAdU&@zk^x)*-G*_M3{y` zS_cS#;tB*peF*}^VNKT{kgAIii0LW>GI1FKAzp_7pf5zApeqr`+`C%EvmGTjFOvCq z_X8~m+2d3n6UH19CUfNGnB;F9xj82JTSsn=NuE1$b4>C=%PCj(?m&6Rrc+vAdZPuV z8(Oe`p*QY_sTa2z4ZZW-qppVIg^uEQ3eUc&ry<&1x8L-Xw7f~W?v^s}>A8eb`nz5} z5hZ^U;h3;wfutq|Bb0ll*Au?D(Sz~hxzw+bo47zvIpP& zQ%V15Me<13s>P!GOI`3jmGsA-lMkem{SHN1w6wfBZ?=pZ{kD&L7zR Fe*qDKyLJEo literal 0 HcmV?d00001 diff --git a/pc-bios/core3-reset b/pc-bios/core3-reset new file mode 100755 index 0000000000000000000000000000000000000000..fb5daad336e9511424129da3c61cc2197c59e3d3 GIT binary patch literal 5032 zcmeHLziU)M5dL=WE)C6$sjCZ>1?L@>hthm9e|xHl9Ql^^x#%?2rTnf&xK-pg>R{C=e6~ z3Iqj$0zrYGKu{nk@c$L4*S|kvKW;VW06qqMJ%H%X58sS|?R}a)3B3NGBS+_WdFg5M zfOx0ea(r%j?A>lMeCdDu_P$peJ)-kr|L5nO-oo&^_>Jp1NjpHXC2z|k$?dOK0Y zuR)Qg&sPk<@UXerl4cUCsY*rbd749rR-TJoC7FVd?L;LMHrhZV-&$8mi=xVGT=OR- zHx(LkO+3qzb?IODZ*iK+wYEdPjn~WVhrB-iTJM>PR;NVQa|in0t?TOV70Gli*Iy;9 zQsU^er!UCv*l_0ctfCcX7jeJWI(4Alh3W6;=hGj^*eLmn=6`bYV@o^p<@tXA83d@I literal 0 HcmV?d00001 diff --git a/pc-bios/core4-hmcode b/pc-bios/core4-hmcode new file mode 100755 index 0000000000000000000000000000000000000000..668a9e97b3a39e9bd44273860d1f35505025ec71 GIT binary patch literal 243040 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 literal 0 HcmV?d00001 diff --git a/pc-bios/meson.build b/pc-bios/meson.build index b40ff3f2bd..05e9065ad6 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -38,6 +38,9 @@ blobs = files( 'vgabios-ramfb.bin', 'vgabios-bochs-display.bin', 'vgabios-ati.bin', + 'uefi-bios-sw', + 'core3-reset', + 'core3-hmcode', 'openbios-sparc32', 'openbios-sparc64', 'openbios-ppc', diff --git a/pc-bios/uefi-bios-sw b/pc-bios/uefi-bios-sw new file mode 100755 index 0000000000000000000000000000000000000000..d99d3ae58fa67db329d2d241b8eb94948d84a7e4 GIT binary patch literal 3145728 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�yJ!is38=9j?dv$r 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 z

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�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 _0ij)@sX5=5DMU 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