From 28760edcd9589c1d8a39b25993ec46128865ca30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:38 +0100 Subject: [PATCH 001/412] tests/qtest: add various qtest_qmp_assert_success() variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add several counterparts of qtest_qmp_assert_success() that can * Use va_list instead of ... * Accept a list of FDs to send * Return the response data Reviewed-by: Thomas Huth Reviewed-by: Juan Quintela Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-2-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/libqtest.c | 97 +++++++++++++++++++++++++++++++--- tests/qtest/libqtest.h | 115 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 7 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index c3a0ef5bb4..7ae28fb6ac 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1229,25 +1229,108 @@ void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size) qtest_rsp(s); } -void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) +QDict *qtest_vqmp_assert_success_ref(QTestState *qts, + const char *fmt, va_list args) { - va_list ap; QDict *response; + QDict *ret; - va_start(ap, fmt); - response = qtest_vqmp(qts, fmt, ap); - va_end(ap); + response = qtest_vqmp(qts, fmt, args); g_assert(response); if (!qdict_haskey(response, "return")) { - GString *s = qobject_to_json_pretty(QOBJECT(response), true); + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true); g_test_message("%s", s->str); - g_string_free(s, true); } g_assert(qdict_haskey(response, "return")); + ret = qdict_get_qdict(response, "return"); + qobject_ref(ret); + qobject_unref(response); + + return ret; +} + +void qtest_vqmp_assert_success(QTestState *qts, + const char *fmt, va_list args) +{ + QDict *response; + + response = qtest_vqmp_assert_success_ref(qts, fmt, args); + qobject_unref(response); } +#ifndef _WIN32 +QDict *qtest_vqmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) +{ + QDict *response; + QDict *ret; + + response = qtest_vqmp_fds(qts, fds, nfds, fmt, args); + + g_assert(response); + if (!qdict_haskey(response, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true); + g_test_message("%s", s->str); + } + g_assert(qdict_haskey(response, "return")); + ret = qdict_get_qdict(response, "return"); + qobject_ref(ret); + qobject_unref(response); + + return ret; +} + +void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) +{ + QDict *response; + response = qtest_vqmp_fds_assert_success_ref(qts, fds, nfds, fmt, args); + qobject_unref(response); +} +#endif /* !_WIN32 */ + +QDict *qtest_qmp_assert_success_ref(QTestState *qts, const char *fmt, ...) +{ + QDict *response; + va_list ap; + va_start(ap, fmt); + response = qtest_vqmp_assert_success_ref(qts, fmt, ap); + va_end(ap); + return response; +} + +void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + qtest_vqmp_assert_success(qts, fmt, ap); + va_end(ap); +} + +#ifndef _WIN32 +QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) +{ + QDict *response; + va_list ap; + va_start(ap, fmt); + response = qtest_vqmp_fds_assert_success_ref(qts, fds, nfds, fmt, ap); + va_end(ap); + return response; +} + +void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + qtest_vqmp_fds_assert_success(qts, fds, nfds, fmt, ap); + va_end(ap); +} +#endif /* !_WIN32 */ + bool qtest_big_endian(QTestState *s) { return s->big_endian; diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 8d7d450963..d310eba7fb 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -693,6 +693,86 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data); */ void qtest_remove_abrt_handler(void *data); +/** + * qtest_vqmp_assert_success_ref: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message to QEMU, asserts that a 'return' key is present in + * the response, and returns the response. + */ +QDict *qtest_vqmp_assert_success_ref(QTestState *qts, + const char *fmt, va_list args) + G_GNUC_PRINTF(2, 0); + +/** + * qtest_vqmp_assert_success: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message to QEMU and asserts that a 'return' key is present in + * the response. + */ +void qtest_vqmp_assert_success(QTestState *qts, + const char *fmt, va_list args) + G_GNUC_PRINTF(2, 0); + +#ifndef _WIN32 +/** + * qtest_vqmp_fds_assert_success_ref: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message with file descriptors to QEMU, + * asserts that a 'return' key is present in the response, + * and returns the response. + */ +QDict *qtest_vqmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) + G_GNUC_PRINTF(4, 0); + +/** + * qtest_vqmp_fds_assert_success: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message with file descriptors to QEMU and + * asserts that a 'return' key is present in the response. + */ +void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) + G_GNUC_PRINTF(4, 0); +#endif /* !_WIN32 */ + +/** + * qtest_qmp_assert_success_ref: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU, asserts that a 'return' key is present in + * the response, and returns the response. + */ +QDict *qtest_qmp_assert_success_ref(QTestState *qts, const char *fmt, ...) + G_GNUC_PRINTF(2, 3); + /** * qtest_qmp_assert_success: * @qts: QTestState instance to operate on @@ -706,6 +786,41 @@ void qtest_remove_abrt_handler(void *data); void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +#ifndef _WIN32 +/** + * qtest_qmp_fd_assert_success_ref: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message with file descriptors to QEMU, + * asserts that a 'return' key is present in the response, + * and returns the response. + */ +QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) + G_GNUC_PRINTF(4, 5); + +/** + * qtest_qmp_fd_assert_success: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message with file descriptors to QEMU and + * asserts that a 'return' key is present in the response. + */ +void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) + G_GNUC_PRINTF(4, 5); +#endif /* !_WIN32 */ + /** * qtest_cb_for_every_machine: * @cb: Pointer to the callback function From 0150e75d012b34182fa1951d346377f8196464e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:39 +0100 Subject: [PATCH 002/412] tests/qtest: add support for callback to receive QMP events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently code must call one of the qtest_qmp_event* functions to fetch events. These are only usable if the immediate caller knows the particular event they want to capture, and are only interested in one specific event type. Adding ability to register an event callback lets the caller capture a range of events over any period of time. Reviewed-by: Juan Quintela Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-3-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/libqtest.c | 18 ++++++++++++++++-- tests/qtest/libqtest.h | 43 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 7ae28fb6ac..77de16227f 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -82,6 +82,8 @@ struct QTestState GString *rx; QTestTransportOps ops; GList *pending_events; + QTestQMPEventCallback eventCB; + void *eventData; }; static GHookList abrt_hooks; @@ -703,8 +705,13 @@ QDict *qtest_qmp_receive(QTestState *s) if (!qdict_get_try_str(response, "event")) { return response; } - /* Stash the event for a later consumption */ - s->pending_events = g_list_append(s->pending_events, response); + + if (!s->eventCB || + !s->eventCB(s, qdict_get_str(response, "event"), + response, s->eventData)) { + /* Stash the event for a later consumption */ + s->pending_events = g_list_append(s->pending_events, response); + } } } @@ -808,6 +815,13 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) va_end(ap); } +void qtest_qmp_set_event_callback(QTestState *s, + QTestQMPEventCallback cb, void *opaque) +{ + s->eventCB = cb; + s->eventData = opaque; +} + QDict *qtest_qmp_event_ref(QTestState *s, const char *event) { while (s->pending_events) { diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index d310eba7fb..a12acf7fa9 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -238,17 +238,52 @@ QDict *qtest_qmp_receive_dict(QTestState *s); * @s: #QTestState instance to operate on. * * Reads a QMP message from QEMU and returns the response. - * Buffers all the events received meanwhile, until a - * call to qtest_qmp_eventwait + * + * If a callback is registered with qtest_qmp_set_event_callback, + * it will be invoked for every event seen, otherwise events + * will be buffered until a call to one of the qtest_qmp_eventwait + * family of functions. */ QDict *qtest_qmp_receive(QTestState *s); +/* + * QTestQMPEventCallback: + * @s: #QTestState instance event was received on + * @name: name of the event type + * @event: #QDict for the event details + * @opaque: opaque data from time of callback registration + * + * This callback will be invoked whenever an event is received. + * If the callback returns true the event will be consumed, + * otherwise it will be put on the list of pending events. + * Pending events can be later handled by calling either + * qtest_qmp_eventwait or qtest_qmp_eventwait_ref. + * + * Return: true to consume the event, false to let it be queued + */ +typedef bool (*QTestQMPEventCallback)(QTestState *s, const char *name, + QDict *event, void *opaque); + +/** + * qtest_qmp_set_event_callback: + * @s: #QTestSTate instance to operate on + * @cb: callback to invoke for events + * @opaque: data to pass to @cb + * + * Register a callback to be invoked whenever an event arrives + */ +void qtest_qmp_set_event_callback(QTestState *s, + QTestQMPEventCallback cb, void *opaque); + /** * qtest_qmp_eventwait: * @s: #QTestState instance to operate on. * @event: event to wait for. * * Continuously polls for QMP responses until it receives the desired event. + * + * Any callback registered with qtest_qmp_set_event_callback will + * be invoked for every event seen. */ void qtest_qmp_eventwait(QTestState *s, const char *event); @@ -258,6 +293,10 @@ void qtest_qmp_eventwait(QTestState *s, const char *event); * @event: event to wait for. * * Continuously polls for QMP responses until it receives the desired event. + * + * Any callback registered with qtest_qmp_set_event_callback will + * be invoked for every event seen. + * * Returns a copy of the event for further investigation. */ QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event); From ffd47275895633f2f21aa5f86ab184776b5ea9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:40 +0100 Subject: [PATCH 003/412] tests/qtest: get rid of 'qmp_command' helper in migration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function duplicates logic of qtest_qmp_assert_success_ref. The qtest_qmp_assert_success_ref method has better diagnostics on failure because it prints the entire QMP response, instead of just asserting on existance of the 'error' key. Reviewed-by: Juan Quintela Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-4-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-helpers.c | 22 ---------------------- tests/qtest/migration-helpers.h | 3 --- tests/qtest/migration-test.c | 29 +++++++++++++++-------------- 3 files changed, 15 insertions(+), 39 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index f6f3c6680f..bddf3f8d4d 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -85,28 +85,6 @@ QDict *wait_command(QTestState *who, const char *command, ...) return ret; } -/* - * Execute the qmp command only - */ -QDict *qmp_command(QTestState *who, const char *command, ...) -{ - va_list ap; - QDict *resp, *ret; - - va_start(ap, command); - resp = qtest_vqmp(who, command, ap); - va_end(ap); - - g_assert(!qdict_haskey(resp, "error")); - g_assert(qdict_haskey(resp, "return")); - - ret = qdict_get_qdict(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); - - return ret; -} - /* * Send QMP command "migrate". * Arguments are built from @fmt... (formatted like diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index a188b62787..2e51a6e195 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -25,9 +25,6 @@ QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); G_GNUC_PRINTF(2, 3) QDict *wait_command(QTestState *who, const char *command, ...); -G_GNUC_PRINTF(2, 3) -QDict *qmp_command(QTestState *who, const char *command, ...); - G_GNUC_PRINTF(3, 4) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index b99b49a314..9ce27f89ec 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -2322,32 +2322,33 @@ static void test_multifd_tcp_cancel(void) static void calc_dirty_rate(QTestState *who, uint64_t calc_time) { - qobject_unref(qmp_command(who, - "{ 'execute': 'calc-dirty-rate'," - "'arguments': { " - "'calc-time': %" PRIu64 "," - "'mode': 'dirty-ring' }}", - calc_time)); + qtest_qmp_assert_success(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %" PRIu64 "," + "'mode': 'dirty-ring' }}", + calc_time); } static QDict *query_dirty_rate(QTestState *who) { - return qmp_command(who, "{ 'execute': 'query-dirty-rate' }"); + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); } static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) { - qobject_unref(qmp_command(who, - "{ 'execute': 'set-vcpu-dirty-limit'," - "'arguments': { " - "'dirty-rate': %" PRIu64 " } }", - dirtyrate)); + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); } static void cancel_vcpu_dirty_limit(QTestState *who) { - qobject_unref(qmp_command(who, - "{ 'execute': 'cancel-vcpu-dirty-limit' }")); + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); } static QDict *query_vcpu_dirty_limit(QTestState *who) From 11936f0ef6b29b1e7ba05ad888dd585210d74fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:41 +0100 Subject: [PATCH 004/412] tests/qtest: get rid of some 'qtest_qmp' usage in migration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the usage is just a verbose way of re-inventing the qtest_qmp_assert_success(_ref) methods. Reviewed-by: Thomas Huth Reviewed-by: Juan Quintela Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-5-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-helpers.c | 8 ++--- tests/qtest/migration-test.c | 52 ++++++++++++--------------------- 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index bddf3f8d4d..e26fdcb132 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -93,7 +93,7 @@ QDict *wait_command(QTestState *who, const char *command, ...) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) { va_list ap; - QDict *args, *rsp; + QDict *args; va_start(ap, fmt); args = qdict_from_vjsonf_nofail(fmt, ap); @@ -102,10 +102,8 @@ void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) g_assert(!qdict_haskey(args, "uri")); qdict_put_str(args, "uri", uri); - rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate', 'arguments': %p}", args); } /* diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 9ce27f89ec..822516286d 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -359,14 +359,10 @@ static void migrate_check_parameter_int(QTestState *who, const char *parameter, static void migrate_set_parameter_int(QTestState *who, const char *parameter, long long value) { - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); migrate_check_parameter_int(who, parameter, value); } @@ -392,14 +388,10 @@ static void migrate_check_parameter_str(QTestState *who, const char *parameter, static void migrate_set_parameter_str(QTestState *who, const char *parameter, const char *value) { - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %s } }", - parameter, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %s } }", + parameter, value); migrate_check_parameter_str(who, parameter, value); } @@ -427,14 +419,10 @@ static void migrate_check_parameter_bool(QTestState *who, const char *parameter, static void migrate_set_parameter_bool(QTestState *who, const char *parameter, int value) { - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %i } }", - parameter, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %i } }", + parameter, value); migrate_check_parameter_bool(who, parameter, value); } @@ -494,16 +482,12 @@ static void migrate_cancel(QTestState *who) static void migrate_set_capability(QTestState *who, const char *capability, bool value) { - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); } static void migrate_postcopy_start(QTestState *from, QTestState *to) From cdf5ab55872438bb3996d2b2a439c3a7b7425aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:42 +0100 Subject: [PATCH 005/412] tests/qtest: switch to using event callbacks for STOP event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the migration test to use the new qtest event callback to watch for the stop event. This ensures that we only watch for the STOP event on the source QEMU. The previous code would set the single 'got_stop' flag when either source or dest QEMU got the STOP event. Reviewed-by: Juan Quintela Acked-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-6-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-helpers.c | 19 +++++++++---------- tests/qtest/migration-helpers.h | 3 ++- tests/qtest/migration-test.c | 4 ++++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index e26fdcb132..7ceadecf84 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -23,15 +23,17 @@ */ #define MIGRATION_STATUS_WAIT_TIMEOUT 120 -bool got_stop; - -static void check_stop_event(QTestState *who) +bool migrate_watch_for_stop(QTestState *who, const char *name, + QDict *event, void *opaque) { - QDict *event = qtest_qmp_event_ref(who, "STOP"); - if (event) { - got_stop = true; - qobject_unref(event); + bool *seen = opaque; + + if (g_str_equal(name, "STOP")) { + *seen = true; + return true; } + + return false; } #ifndef _WIN32 @@ -48,7 +50,6 @@ QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) va_end(ap); resp = qtest_qmp_receive(who); - check_stop_event(who); g_assert(!qdict_haskey(resp, "error")); g_assert(qdict_haskey(resp, "return")); @@ -73,8 +74,6 @@ QDict *wait_command(QTestState *who, const char *command, ...) resp = qtest_vqmp(who, command, ap); va_end(ap); - check_stop_event(who); - g_assert(!qdict_haskey(resp, "error")); g_assert(qdict_haskey(resp, "return")); diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 2e51a6e195..fa69d1780a 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -15,7 +15,8 @@ #include "libqtest.h" -extern bool got_stop; +bool migrate_watch_for_stop(QTestState *who, const char *name, + QDict *event, void *opaque); #ifndef _WIN32 G_GNUC_PRINTF(3, 4) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 822516286d..0af72c37c2 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -43,6 +43,7 @@ unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; +static bool got_stop; /* * Dirtylimit stop working if dirty page rate error @@ -703,6 +704,9 @@ static int test_migrate_start(QTestState **from, QTestState **to, ignore_stderr); if (!args->only_target) { *from = qtest_init(cmd_source); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_stop, + &got_stop); } cmd_target = g_strdup_printf("-accel kvm%s -accel tcg%s%s " From aca040695890fe67b44402fdb167809809946068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:43 +0100 Subject: [PATCH 006/412] tests/qtest: replace wait_command() with qtest_qmp_assert_success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most usage of wait_command() is followed by qobject_unref(), which is just a verbose re-implementation of qtest_qmp_assert_success(). Reviewed-by: Thomas Huth Reviewed-by: Juan Quintela Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-7-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-helpers.c | 53 +--------- tests/qtest/migration-helpers.h | 8 -- tests/qtest/migration-test.c | 170 +++++++++++++------------------- 3 files changed, 74 insertions(+), 157 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 7ceadecf84..73e506a5f8 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -36,54 +36,6 @@ bool migrate_watch_for_stop(QTestState *who, const char *name, return false; } -#ifndef _WIN32 -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) -{ - va_list ap; - QDict *resp, *ret; - - va_start(ap, command); - qtest_qmp_vsend_fds(who, &fd, 1, command, ap); - va_end(ap); - - resp = qtest_qmp_receive(who); - - g_assert(!qdict_haskey(resp, "error")); - g_assert(qdict_haskey(resp, "return")); - - ret = qdict_get_qdict(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); - - return ret; -} -#endif - -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command(QTestState *who, const char *command, ...) -{ - va_list ap; - QDict *resp, *ret; - - va_start(ap, command); - resp = qtest_vqmp(who, command, ap); - va_end(ap); - - g_assert(!qdict_haskey(resp, "error")); - g_assert(qdict_haskey(resp, "return")); - - ret = qdict_get_qdict(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); - - return ret; -} - /* * Send QMP command "migrate". * Arguments are built from @fmt... (formatted like @@ -111,7 +63,7 @@ void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) */ QDict *migrate_query(QTestState *who) { - return wait_command(who, "{ 'execute': 'query-migrate' }"); + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); } QDict *migrate_query_not_failed(QTestState *who) @@ -209,7 +161,8 @@ void wait_for_migration_fail(QTestState *from, bool allow_active) } while (!failed); /* Is the machine currently running? */ - rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); + rsp_return = qtest_qmp_assert_success_ref(from, + "{ 'execute': 'query-status' }"); g_assert(qdict_haskey(rsp_return, "running")); g_assert(qdict_get_bool(rsp_return, "running")); qobject_unref(rsp_return); diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index fa69d1780a..aab0745cfe 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -18,14 +18,6 @@ bool migrate_watch_for_stop(QTestState *who, const char *name, QDict *event, void *opaque); -#ifndef _WIN32 -G_GNUC_PRINTF(3, 4) -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); -#endif - -G_GNUC_PRINTF(2, 3) -QDict *wait_command(QTestState *who, const char *command, ...); - G_GNUC_PRINTF(3, 4) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0af72c37c2..822cf13536 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -342,7 +342,8 @@ static long long migrate_get_parameter_int(QTestState *who, QDict *rsp; long long result; - rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); result = qdict_get_int(rsp, parameter); qobject_unref(rsp); return result; @@ -373,7 +374,8 @@ static char *migrate_get_parameter_str(QTestState *who, QDict *rsp; char *result; - rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); result = g_strdup(qdict_get_str(rsp, parameter)); qobject_unref(rsp); return result; @@ -402,7 +404,8 @@ static long long migrate_get_parameter_bool(QTestState *who, QDict *rsp; int result; - rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); result = qdict_get_bool(rsp, parameter); qobject_unref(rsp); return !!result; @@ -443,41 +446,29 @@ static void migrate_ensure_converge(QTestState *who) static void migrate_pause(QTestState *who) { - QDict *rsp; - - rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); - qobject_unref(rsp); + qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); } static void migrate_continue(QTestState *who, const char *state) { - QDict *rsp; - - rsp = wait_command(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); } static void migrate_recover(QTestState *who, const char *uri) { - QDict *rsp; - - rsp = wait_command(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); } static void migrate_cancel(QTestState *who) { - QDict *rsp; - - rsp = wait_command(who, "{ 'execute': 'migrate_cancel' }"); - qobject_unref(rsp); + qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); } static void migrate_set_capability(QTestState *who, const char *capability, @@ -493,10 +484,7 @@ static void migrate_set_capability(QTestState *who, const char *capability, static void migrate_postcopy_start(QTestState *from, QTestState *to) { - QDict *rsp; - - rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); - qobject_unref(rsp); + qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); if (!got_stop) { qtest_qmp_eventwait(from, "STOP"); @@ -785,7 +773,6 @@ test_migrate_tls_psk_start_common(QTestState *from, { struct TestMigrateTLSPSKData *data = g_new0(struct TestMigrateTLSPSKData, 1); - QDict *rsp; data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); data->pskfile = g_strdup_printf("%s/%s", data->workdir, @@ -801,24 +788,22 @@ test_migrate_tls_psk_start_common(QTestState *from, test_tls_psk_init_alt(data->pskfilealt); } - rsp = wait_command(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'username': 'qemu'} }", - data->workdir); - qobject_unref(rsp); + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'username': 'qemu'} }", + data->workdir); - rsp = wait_command(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'server'," - " 'dir': %s } }", - mismatch ? data->workdiralt : data->workdir); - qobject_unref(rsp); + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'server'," + " 'dir': %s } }", + mismatch ? data->workdiralt : data->workdir); migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); @@ -889,7 +874,6 @@ test_migrate_tls_x509_start_common(QTestState *from, TestMigrateTLSX509 *args) { TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); - QDict *rsp; data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); @@ -932,40 +916,38 @@ test_migrate_tls_x509_start_common(QTestState *from, args->certhostname, args->certipaddr); - rsp = wait_command(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509client0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': true} }", - data->workdir); - qobject_unref(rsp); + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509client0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': true} }", + data->workdir); migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); if (args->certhostname) { migrate_set_parameter_str(from, "tls-hostname", args->certhostname); } - rsp = wait_command(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509server0'," - " 'endpoint': 'server'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': %i} }", - data->workdir, args->verifyclient); - qobject_unref(rsp); + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509server0'," + " 'endpoint': 'server'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': %i} }", + data->workdir, args->verifyclient); migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); if (args->authzclient) { - rsp = wait_command(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'authz-simple'," - " 'id': 'tlsauthz0'," - " 'identity': %s} }", - "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'authz-simple'," + " 'id': 'tlsauthz0'," + " 'identity': %s} }", + "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); } @@ -1759,7 +1741,6 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) static void *test_migrate_fd_start_hook(QTestState *from, QTestState *to) { - QDict *rsp; int ret; int pair[2]; @@ -1768,22 +1749,19 @@ static void *test_migrate_fd_start_hook(QTestState *from, g_assert_cmpint(ret, ==, 0); /* Send the 1st socket to the target */ - rsp = wait_command_fd(to, pair[0], - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qobject_unref(rsp); + qtest_qmp_fds_assert_success(to, &pair[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); close(pair[0]); /* Start incoming migration from the 1st socket */ - rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'fd:fd-mig' }}"); - qobject_unref(rsp); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'fd:fd-mig' }}"); /* Send the 2nd socket to the target */ - rsp = wait_command_fd(from, pair[1], - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qobject_unref(rsp); + qtest_qmp_fds_assert_success(from, &pair[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); close(pair[1]); return NULL; @@ -1990,8 +1968,6 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from, QTestState *to, const char *method) { - QDict *rsp; - migrate_set_parameter_int(from, "multifd-channels", 16); migrate_set_parameter_int(to, "multifd-channels", 16); @@ -2002,9 +1978,8 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from, migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ - rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); - qobject_unref(rsp); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); return NULL; } @@ -2235,7 +2210,6 @@ static void test_multifd_tcp_cancel(void) .hide_stderr = true, }; QTestState *from, *to, *to2; - QDict *rsp; g_autofree char *uri = NULL; if (test_migrate_start(&from, &to, "defer", &args)) { @@ -2251,9 +2225,8 @@ static void test_multifd_tcp_cancel(void) migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ - rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); - qobject_unref(rsp); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -2283,9 +2256,8 @@ static void test_multifd_tcp_cancel(void) migrate_set_capability(to2, "multifd", true); /* Start incoming migration from the 1st socket */ - rsp = wait_command(to2, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); - qobject_unref(rsp); + qtest_qmp_assert_success(to2, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); g_free(uri); uri = migrate_get_socket_address(to2, "socket-address"); From 266ea334b2ea96890e268acff1a26b4a71804361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:44 +0100 Subject: [PATCH 007/412] tests/qtest: capture RESUME events during migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running migration tests we monitor for a STOP event so we can skip redundant waits. This will be needed for the RESUME event too shortly. Reviewed-by: Juan Quintela Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-8-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-helpers.c | 13 +++++++++++++ tests/qtest/migration-helpers.h | 2 ++ tests/qtest/migration-test.c | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 73e506a5f8..be00c52d00 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -36,6 +36,19 @@ bool migrate_watch_for_stop(QTestState *who, const char *name, return false; } +bool migrate_watch_for_resume(QTestState *who, const char *name, + QDict *event, void *opaque) +{ + bool *seen = opaque; + + if (g_str_equal(name, "RESUME")) { + *seen = true; + return true; + } + + return false; +} + /* * Send QMP command "migrate". * Arguments are built from @fmt... (formatted like diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index aab0745cfe..009e250e90 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -17,6 +17,8 @@ bool migrate_watch_for_stop(QTestState *who, const char *name, QDict *event, void *opaque); +bool migrate_watch_for_resume(QTestState *who, const char *name, + QDict *event, void *opaque); G_GNUC_PRINTF(3, 4) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 822cf13536..0948d13e14 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -44,6 +44,7 @@ unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; static bool got_stop; +static bool got_resume; /* * Dirtylimit stop working if dirty page rate error @@ -607,6 +608,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, } got_stop = false; + got_resume = false; bootpath = g_strdup_printf("%s/bootsect", tmpfs); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { /* the assembled x86 boot sector should be exactly one sector large */ @@ -712,6 +714,9 @@ static int test_migrate_start(QTestState **from, QTestState **to, args->opts_target ? args->opts_target : "", ignore_stderr); *to = qtest_init(cmd_target); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_resume, + &got_resume); /* * Remove shmem file immediately to avoid memory leak in test failed case. From 95014994e1a5846c55ee9f1bf9ddea03ac854dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:45 +0100 Subject: [PATCH 008/412] tests/qtest: distinguish src/dst migration VM stop/resume events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'got_stop' and 'got_resume' global variables apply to the src and dst migration VM respectively. Change their names to make this explicit to developers. Reviewed-by: Juan Quintela Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-9-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-test.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0948d13e14..23fb61506c 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -43,8 +43,8 @@ unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; -static bool got_stop; -static bool got_resume; +static bool got_src_stop; +static bool got_dst_resume; /* * Dirtylimit stop working if dirty page rate error @@ -227,7 +227,7 @@ static void wait_for_migration_pass(QTestState *who) uint64_t pass; /* Wait for the 1st sync */ - while (!got_stop && !initial_pass) { + while (!got_src_stop && !initial_pass) { usleep(1000); initial_pass = get_migration_pass(who); } @@ -235,7 +235,7 @@ static void wait_for_migration_pass(QTestState *who) do { usleep(1000); pass = get_migration_pass(who); - } while (pass == initial_pass && !got_stop); + } while (pass == initial_pass && !got_src_stop); } static void check_guests_ram(QTestState *who) @@ -487,7 +487,7 @@ static void migrate_postcopy_start(QTestState *from, QTestState *to) { qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); - if (!got_stop) { + if (!got_src_stop) { qtest_qmp_eventwait(from, "STOP"); } @@ -607,8 +607,8 @@ static int test_migrate_start(QTestState **from, QTestState **to, } } - got_stop = false; - got_resume = false; + got_src_stop = false; + got_dst_resume = false; bootpath = g_strdup_printf("%s/bootsect", tmpfs); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { /* the assembled x86 boot sector should be exactly one sector large */ @@ -696,7 +696,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, *from = qtest_init(cmd_source); qtest_qmp_set_event_callback(*from, migrate_watch_for_stop, - &got_stop); + &got_src_stop); } cmd_target = g_strdup_printf("-accel kvm%s -accel tcg%s%s " @@ -716,7 +716,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, *to = qtest_init(cmd_target); qtest_qmp_set_event_callback(*to, migrate_watch_for_resume, - &got_resume); + &got_dst_resume); /* * Remove shmem file immediately to avoid memory leak in test failed case. @@ -1427,7 +1427,7 @@ static void test_precopy_common(MigrateCommon *args) * hanging forever if migration didn't converge */ wait_for_migration_complete(from); - if (!got_stop) { + if (!got_src_stop) { qtest_qmp_eventwait(from, "STOP"); } @@ -1537,7 +1537,7 @@ static void test_ignore_shared(void) wait_for_migration_pass(from); - if (!got_stop) { + if (!got_src_stop) { qtest_qmp_eventwait(from, "STOP"); } @@ -1942,7 +1942,7 @@ static void test_migrate_auto_converge(void) break; } usleep(20); - g_assert_false(got_stop); + g_assert_false(got_src_stop); } while (true); /* The first percentage of throttling should be at least init_pct */ g_assert_cmpint(percentage, >=, init_pct); @@ -2275,7 +2275,7 @@ static void test_multifd_tcp_cancel(void) wait_for_migration_pass(from); - if (!got_stop) { + if (!got_src_stop) { qtest_qmp_eventwait(from, "STOP"); } qtest_qmp_eventwait(to2, "RESUME"); From 3c4fb177237a115c85c439422f08316e4b8d0d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 1 Jun 2023 17:13:46 +0100 Subject: [PATCH 009/412] tests/qtest: make more migration pre-copy scenarios run non-live MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are 27 pre-copy live migration scenarios being tested. In all of these we force non-convergence and run for one iteration, then let it converge and wait for completion during the second (or following) iterations. At 3 mbps bandwidth limit the first iteration takes a very long time (~30 seconds). While it is important to test the migration passes and convergence logic, it is overkill to do this for all 27 pre-copy scenarios. The TLS migration scenarios in particular are merely exercising different code paths during connection establishment. To optimize time taken, switch most of the test scenarios to run non-live (ie guest CPUs paused) with no bandwidth limits. This gives a massive speed up for most of the test scenarios. For test coverage the following scenarios are unchanged * Precopy with UNIX sockets * Precopy with UNIX sockets and dirty ring tracking * Precopy with XBZRLE * Precopy with UNIX compress * Precopy with UNIX compress (nowait) * Precopy with multifd On a test machine this reduces execution time from 13 minutes to 8 minutes. Tested-by: Thomas Huth Reviewed-by: Juan Quintela Reviewed-by: Peter Xu Signed-off-by: Daniel P. Berrangé Message-Id: <20230601161347.1803440-10-berrange@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-test.c | 83 +++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 23fb61506c..0b9d045152 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -577,9 +577,12 @@ typedef struct { MIG_TEST_FAIL_DEST_QUIT_ERR, } result; - /* Optional: set number of migration passes to wait for */ + /* Optional: set number of migration passes to wait for, if live==true */ unsigned int iterations; + /* Optional: whether the guest CPUs should be running during migration */ + bool live; + /* Postcopy specific fields */ void *postcopy_data; bool postcopy_preempt; @@ -1385,8 +1388,6 @@ static void test_precopy_common(MigrateCommon *args) return; } - migrate_ensure_non_converge(from); - if (args->start_hook) { data_hook = args->start_hook(from, to); } @@ -1396,6 +1397,31 @@ static void test_precopy_common(MigrateCommon *args) wait_for_serial("src_serial"); } + if (args->live) { + /* + * Testing live migration, we want to ensure that some + * memory is re-dirtied after being transferred, so that + * we exercise logic for dirty page handling. We achieve + * this with a ridiculosly low bandwidth that guarantees + * non-convergance. + */ + migrate_ensure_non_converge(from); + } else { + /* + * Testing non-live migration, we allow it to run at + * full speed to ensure short test case duration. + * For tests expected to fail, we don't need to + * change anything. + */ + if (args->result == MIG_TEST_SUCCEED) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + if (!got_src_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + migrate_ensure_converge(from); + } + } + if (!args->connect_uri) { g_autofree char *local_connect_uri = migrate_get_socket_address(to, "socket-address"); @@ -1413,26 +1439,42 @@ static void test_precopy_common(MigrateCommon *args) qtest_set_expected_status(to, EXIT_FAILURE); } } else { - if (args->iterations) { - while (args->iterations--) { + if (args->live) { + if (args->iterations) { + while (args->iterations--) { + wait_for_migration_pass(from); + } + } else { wait_for_migration_pass(from); } + + migrate_ensure_converge(from); + + /* + * We do this first, as it has a timeout to stop us + * hanging forever if migration didn't converge + */ + wait_for_migration_complete(from); + + if (!got_src_stop) { + qtest_qmp_eventwait(from, "STOP"); + } } else { - wait_for_migration_pass(from); + wait_for_migration_complete(from); + /* + * Must wait for dst to finish reading all incoming + * data on the socket before issuing 'cont' otherwise + * it'll be ignored + */ + wait_for_migration_complete(to); + + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); } - migrate_ensure_converge(from); - - /* We do this first, as it has a timeout to stop us - * hanging forever if migration didn't converge */ - wait_for_migration_complete(from); - - if (!got_src_stop) { - qtest_qmp_eventwait(from, "STOP"); + if (!got_dst_resume) { + qtest_qmp_eventwait(to, "RESUME"); } - qtest_qmp_eventwait(to, "RESUME"); - wait_for_serial("dest_serial"); } @@ -1449,6 +1491,8 @@ static void test_precopy_unix_plain(void) MigrateCommon args = { .listen_uri = uri, .connect_uri = uri, + + .live = true, }; test_precopy_common(&args); @@ -1464,6 +1508,8 @@ static void test_precopy_unix_dirty_ring(void) }, .listen_uri = uri, .connect_uri = uri, + + .live = true, }; test_precopy_common(&args); @@ -1575,6 +1621,7 @@ static void test_precopy_unix_xbzrle(void) .start_hook = test_migrate_xbzrle_start, .iterations = 2, + .live = true, }; test_precopy_common(&args); @@ -1592,6 +1639,7 @@ static void test_precopy_unix_compress(void) * the previous iteration. */ .iterations = 2, + .live = true, }; test_precopy_common(&args); @@ -1609,6 +1657,7 @@ static void test_precopy_unix_compress_nowait(void) * the previous iteration. */ .iterations = 2, + .live = true, }; test_precopy_common(&args); @@ -2017,6 +2066,8 @@ static void test_multifd_tcp_none(void) MigrateCommon args = { .listen_uri = "defer", .start_hook = test_migrate_precopy_tcp_multifd_start, + + .live = true, }; test_precopy_common(&args); } From b861383c2690501ff2687f9ef9268b128b0fb3b3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 1 Jun 2023 13:29:35 -0400 Subject: [PATCH 010/412] qtest/migration: Document live=true cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document every single live=true use cases on why it should be done in the live manner. Also document on the parameter so new precopy cases should always use live=off unless with explicit reasonings. Cc: Thomas Huth Cc: Juan Quintela Cc: Daniel P. Berrangé Reviewed-by: Juan Quintela Reviewed-by: Daniel P. Berrangé Signed-off-by: Peter Xu Message-Id: <20230601172935.175726-1-peterx@redhat.com> Signed-off-by: Juan Quintela --- tests/qtest/migration-test.c | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0b9d045152..b0c355bbd9 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -580,7 +580,14 @@ typedef struct { /* Optional: set number of migration passes to wait for, if live==true */ unsigned int iterations; - /* Optional: whether the guest CPUs should be running during migration */ + /* + * Optional: whether the guest CPUs should be running during a precopy + * migration test. We used to always run with live but it took much + * longer so we reduced live tests to only the ones that have solid + * reason to be tested live-only. For each of the new test cases for + * precopy please provide justifications to use live explicitly (please + * refer to existing ones with live=true), or use live=off by default. + */ bool live; /* Postcopy specific fields */ @@ -1491,7 +1498,10 @@ static void test_precopy_unix_plain(void) MigrateCommon args = { .listen_uri = uri, .connect_uri = uri, - + /* + * The simplest use case of precopy, covering smoke tests of + * get-dirty-log dirty tracking. + */ .live = true, }; @@ -1508,7 +1518,10 @@ static void test_precopy_unix_dirty_ring(void) }, .listen_uri = uri, .connect_uri = uri, - + /* + * Besides the precopy/unix basic test, cover dirty ring interface + * rather than get-dirty-log. + */ .live = true, }; @@ -1617,10 +1630,12 @@ static void test_precopy_unix_xbzrle(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_xbzrle_start, - .iterations = 2, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ .live = true, }; @@ -1639,6 +1654,11 @@ static void test_precopy_unix_compress(void) * the previous iteration. */ .iterations = 2, + /* + * We make sure the compressor can always work well even if guest + * memory is changing. See commit 34ab9e9743 where we used to fix + * a bug when only trigger-able with guest memory changing. + */ .live = true, }; @@ -1657,6 +1677,7 @@ static void test_precopy_unix_compress_nowait(void) * the previous iteration. */ .iterations = 2, + /* Same reason for the wait version of precopy compress test */ .live = true, }; @@ -2066,7 +2087,11 @@ static void test_multifd_tcp_none(void) MigrateCommon args = { .listen_uri = "defer", .start_hook = test_migrate_precopy_tcp_multifd_start, - + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ .live = true, }; test_precopy_common(&args); From 430746359fae0ec3e2ac723fd673cb8451b28d7b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 19 May 2023 10:02:16 -0500 Subject: [PATCH 011/412] iotests: Fix test 104 under NBD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the past, commit a231cb27 ("iotests: Fix 104 for NBD", v2.3.0) added an additional filter to _filter_img_info to rewrite NBD URIs into the expected output form. This recently broke when we tweaked tests to run in a per-format directory, which did not match the regex, because _img_info itself is now already changing SOCK_DIR=/tmp/tmpphjfbphd/raw-nbd-104 into /tmp/tmpphjfbphd/IMGFMT-nbd-104 prior to _img_info_filter getting a chance to further filter things. While diagnosing the problem, I also noticed some filter lines rendered completely useless by a typo when we switched from TCP to Unix sockets for NBD (in shell, '\\+' is different from "\\+" (one gives two backslash to the regex, matching the literal 2-byte sequence <\+> after a single digit; the other gives one backslash to the regex, as the metacharacter \+ to match one or more of <[0-9]>); since the literal string is not a valid URI, that regex hasn't been matching anything for years so it is fine to just drop it rather than fix the typo. Fixes: f3923a72 ("iotests: Switch nbd tests to use Unix rather than TCP", v4.2.0) Fixes: 5ba7db09 ("iotests: always use a unique sub-directory per test", v8.0.0) Signed-off-by: Eric Blake Message-Id: <20230519150216.2599189-1-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé --- tests/qemu-iotests/common.filter | 4 +--- tests/qemu-iotests/common.rc | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 6b32c7fbfa..fc3c64bcb8 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright Red Hat # Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or @@ -131,7 +131,6 @@ _filter_img_create_filenames() -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e 's#SOCK_DIR/fuse-#TEST_DIR/#g' \ -e "s#$IMGFMT#IMGFMT#g" \ - -e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' } @@ -229,7 +228,6 @@ _filter_img_info() -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ - -e 's#nbd://127.0.0.1:[0-9]\\+$#TEST_DIR/t.IMGFMT#g' \ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' \ -e 's#SOCK_DIR/fuse-#TEST_DIR/#g' \ -e "/encrypted: yes/d" \ diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index f4476b62f7..d145f08201 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright Red Hat # Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify @@ -717,6 +717,7 @@ _img_info() -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR/fuse-#TEST_DIR/#g" \ + -e "s#$SOCK_DIR/#SOCK_DIR/#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "/^disk size:/ D" \ From 5cf899e21555d57b9ee1d0a8256f9a2230e88658 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 13:46:31 -0500 Subject: [PATCH 012/412] qcow2: Explicit mention of padding bytes Although we already covered the need for padding bytes with our changes in commit 3ae3fcfa, commit 66fcbca5 (both v5.0.0) added one byte and relied on the rest of the text for implicitly covering 7 padding bytes. For consistency with other parts of the header (such as the header extension format listing padding from n - m, or the snapshot table entry listing variable padding), we might as well call out the remaining 7 bytes as padding until such time (as any) as they gain another meaning. Signed-off-by: Eric Blake CC: Vladimir Sementsov-Ogievskiy Message-Id: <20230522184631.47211-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- docs/interop/qcow2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index e7f036c286..2c4618375a 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -226,6 +226,7 @@ version 2. in QEMU. However, clusters with the deflate compression type do not have zlib headers. + 105 - 111: Padding, contents defined below. === Header padding === From 3a5925922520f934f7b0f96d0c65a041540d7738 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 11 May 2023 21:10:15 -0500 Subject: [PATCH 013/412] test-cutils: Avoid g_assert in unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glib documentation[1] is clear: g_assert() should be avoided in unit tests because it is ineffective if G_DISABLE_ASSERT is defined; unit tests should stick to constructs based on g_assert_true() instead. Note that since commit 262a69f428, we intentionally state that you cannot define G_DISABLE_ASSERT while building qemu; but our code can be copied to other projects without that restriction, so we should be consistent. For most of the replacements in this patch, using g_assert_cmpstr() would be a regression in quality - although it would helpfully display the string contents of both pointers on test failure, here, we really do care about pointer equality, not just string content equality. But when a NULL pointer is expected, g_assert_null works fine. [1] https://libsoup.org/glib/glib-Testing.html#g-assert Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230522190441.64278-2-eblake@redhat.com> --- tests/unit/test-cutils.c | 324 +++++++++++++++++++-------------------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 3c4f875420..0202ac0d5b 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -1,7 +1,7 @@ /* * cutils.c unit-tests * - * Copyright (C) 2013 Red Hat Inc. + * Copyright Red Hat * * Authors: * Eduardo Habkost @@ -40,7 +40,7 @@ static void test_parse_uint_null(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_parse_uint_empty(void) @@ -55,7 +55,7 @@ static void test_parse_uint_empty(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_parse_uint_whitespace(void) @@ -70,7 +70,7 @@ static void test_parse_uint_whitespace(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_true(endptr == str); } @@ -86,7 +86,7 @@ static void test_parse_uint_invalid(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_true(endptr == str); } @@ -102,7 +102,7 @@ static void test_parse_uint_trailing(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_parse_uint_correct(void) @@ -117,7 +117,7 @@ static void test_parse_uint_correct(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_octal(void) @@ -132,7 +132,7 @@ static void test_parse_uint_octal(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_decimal(void) @@ -147,7 +147,7 @@ static void test_parse_uint_decimal(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } @@ -163,7 +163,7 @@ static void test_parse_uint_llong_max(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -180,7 +180,7 @@ static void test_parse_uint_overflow(void) g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpint(i, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_negative(void) @@ -195,7 +195,7 @@ static void test_parse_uint_negative(void) g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } @@ -235,7 +235,7 @@ static void test_qemu_strtoi_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoi_null(void) @@ -248,7 +248,7 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoi_empty(void) @@ -262,7 +262,7 @@ static void test_qemu_strtoi_empty(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi_whitespace(void) @@ -276,7 +276,7 @@ static void test_qemu_strtoi_whitespace(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi_invalid(void) @@ -290,7 +290,7 @@ static void test_qemu_strtoi_invalid(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi_trailing(void) @@ -305,7 +305,7 @@ static void test_qemu_strtoi_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoi_octal(void) @@ -320,7 +320,7 @@ static void test_qemu_strtoi_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -328,7 +328,7 @@ static void test_qemu_strtoi_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_decimal(void) @@ -343,7 +343,7 @@ static void test_qemu_strtoi_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -352,7 +352,7 @@ static void test_qemu_strtoi_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_hex(void) @@ -367,7 +367,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -376,7 +376,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -385,7 +385,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoi_max(void) @@ -400,7 +400,7 @@ static void test_qemu_strtoi_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -416,7 +416,7 @@ static void test_qemu_strtoi_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -432,7 +432,7 @@ static void test_qemu_strtoi_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -448,7 +448,7 @@ static void test_qemu_strtoi_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_full_correct(void) @@ -473,7 +473,7 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoi_full_empty(void) @@ -535,7 +535,7 @@ static void test_qemu_strtoui_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoui_null(void) @@ -548,7 +548,7 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoui_empty(void) @@ -562,7 +562,7 @@ static void test_qemu_strtoui_empty(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoui_whitespace(void) @@ -576,7 +576,7 @@ static void test_qemu_strtoui_whitespace(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoui_invalid(void) @@ -590,7 +590,7 @@ static void test_qemu_strtoui_invalid(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoui_trailing(void) @@ -605,7 +605,7 @@ static void test_qemu_strtoui_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoui_octal(void) @@ -620,7 +620,7 @@ static void test_qemu_strtoui_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -628,7 +628,7 @@ static void test_qemu_strtoui_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_decimal(void) @@ -643,7 +643,7 @@ static void test_qemu_strtoui_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -652,7 +652,7 @@ static void test_qemu_strtoui_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_hex(void) @@ -667,7 +667,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -676,7 +676,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -685,7 +685,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoui_max(void) @@ -700,7 +700,7 @@ static void test_qemu_strtoui_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -716,7 +716,7 @@ static void test_qemu_strtoui_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -732,7 +732,7 @@ static void test_qemu_strtoui_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, (unsigned int)-1); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -748,7 +748,7 @@ static void test_qemu_strtoui_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, (unsigned int)-321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_full_correct(void) @@ -830,7 +830,7 @@ static void test_qemu_strtol_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtol_null(void) @@ -843,7 +843,7 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtol_empty(void) @@ -857,7 +857,7 @@ static void test_qemu_strtol_empty(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtol_whitespace(void) @@ -871,7 +871,7 @@ static void test_qemu_strtol_whitespace(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtol_invalid(void) @@ -885,7 +885,7 @@ static void test_qemu_strtol_invalid(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtol_trailing(void) @@ -900,7 +900,7 @@ static void test_qemu_strtol_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtol_octal(void) @@ -915,7 +915,7 @@ static void test_qemu_strtol_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -923,7 +923,7 @@ static void test_qemu_strtol_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_decimal(void) @@ -938,7 +938,7 @@ static void test_qemu_strtol_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -947,7 +947,7 @@ static void test_qemu_strtol_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_hex(void) @@ -962,7 +962,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -971,7 +971,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -980,7 +980,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtol_max(void) @@ -995,7 +995,7 @@ static void test_qemu_strtol_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1011,7 +1011,7 @@ static void test_qemu_strtol_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_underflow(void) @@ -1026,7 +1026,7 @@ static void test_qemu_strtol_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_negative(void) @@ -1041,7 +1041,7 @@ static void test_qemu_strtol_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_full_correct(void) @@ -1066,7 +1066,7 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtol_full_empty(void) @@ -1128,7 +1128,7 @@ static void test_qemu_strtoul_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoul_null(void) @@ -1141,7 +1141,7 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoul_empty(void) @@ -1155,7 +1155,7 @@ static void test_qemu_strtoul_empty(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoul_whitespace(void) @@ -1169,7 +1169,7 @@ static void test_qemu_strtoul_whitespace(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoul_invalid(void) @@ -1183,7 +1183,7 @@ static void test_qemu_strtoul_invalid(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoul_trailing(void) @@ -1198,7 +1198,7 @@ static void test_qemu_strtoul_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoul_octal(void) @@ -1213,7 +1213,7 @@ static void test_qemu_strtoul_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -1221,7 +1221,7 @@ static void test_qemu_strtoul_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_decimal(void) @@ -1236,7 +1236,7 @@ static void test_qemu_strtoul_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -1245,7 +1245,7 @@ static void test_qemu_strtoul_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_hex(void) @@ -1260,7 +1260,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -1269,7 +1269,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -1278,7 +1278,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoul_max(void) @@ -1293,7 +1293,7 @@ static void test_qemu_strtoul_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1309,7 +1309,7 @@ static void test_qemu_strtoul_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_underflow(void) @@ -1324,7 +1324,7 @@ static void test_qemu_strtoul_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, -1ul); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_negative(void) @@ -1339,7 +1339,7 @@ static void test_qemu_strtoul_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, -321ul); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_full_correct(void) @@ -1421,7 +1421,7 @@ static void test_qemu_strtoi64_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoi64_null(void) @@ -1434,7 +1434,7 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoi64_empty(void) @@ -1448,7 +1448,7 @@ static void test_qemu_strtoi64_empty(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_whitespace(void) @@ -1462,7 +1462,7 @@ static void test_qemu_strtoi64_whitespace(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_invalid(void) @@ -1476,7 +1476,7 @@ static void test_qemu_strtoi64_invalid(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_trailing(void) @@ -1491,7 +1491,7 @@ static void test_qemu_strtoi64_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoi64_octal(void) @@ -1506,7 +1506,7 @@ static void test_qemu_strtoi64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); endptr = &f; res = 999; @@ -1514,7 +1514,7 @@ static void test_qemu_strtoi64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_decimal(void) @@ -1529,7 +1529,7 @@ static void test_qemu_strtoi64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; endptr = &f; @@ -1538,7 +1538,7 @@ static void test_qemu_strtoi64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_hex(void) @@ -1553,7 +1553,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; endptr = &f; @@ -1562,7 +1562,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; endptr = &f; @@ -1571,7 +1571,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoi64_max(void) @@ -1586,7 +1586,7 @@ static void test_qemu_strtoi64_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1602,7 +1602,7 @@ static void test_qemu_strtoi64_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_underflow(void) @@ -1617,7 +1617,7 @@ static void test_qemu_strtoi64_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_negative(void) @@ -1632,7 +1632,7 @@ static void test_qemu_strtoi64_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_full_correct(void) @@ -1717,7 +1717,7 @@ static void test_qemu_strtou64_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtou64_null(void) @@ -1730,7 +1730,7 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtou64_empty(void) @@ -1744,7 +1744,7 @@ static void test_qemu_strtou64_empty(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtou64_whitespace(void) @@ -1758,7 +1758,7 @@ static void test_qemu_strtou64_whitespace(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtou64_invalid(void) @@ -1772,7 +1772,7 @@ static void test_qemu_strtou64_invalid(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtou64_trailing(void) @@ -1787,7 +1787,7 @@ static void test_qemu_strtou64_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtou64_octal(void) @@ -1802,7 +1802,7 @@ static void test_qemu_strtou64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); endptr = &f; res = 999; @@ -1810,7 +1810,7 @@ static void test_qemu_strtou64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_decimal(void) @@ -1825,7 +1825,7 @@ static void test_qemu_strtou64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; endptr = &f; @@ -1834,7 +1834,7 @@ static void test_qemu_strtou64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_hex(void) @@ -1849,7 +1849,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; endptr = &f; @@ -1858,7 +1858,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; endptr = &f; @@ -1867,7 +1867,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtou64_max(void) @@ -1882,7 +1882,7 @@ static void test_qemu_strtou64_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1898,7 +1898,7 @@ static void test_qemu_strtou64_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_underflow(void) @@ -1913,7 +1913,7 @@ static void test_qemu_strtou64_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, -1ull); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_negative(void) @@ -1928,7 +1928,7 @@ static void test_qemu_strtou64_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, -321ull); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_full_correct(void) @@ -2013,7 +2013,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); /* Leading 0 gives decimal results, not octal */ str = "08"; @@ -2022,7 +2022,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 8); - g_assert(endptr == str + 2); + g_assert_true(endptr == str + 2); /* Leading space is ignored */ str = " 12345"; @@ -2031,7 +2031,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 6); + g_assert_true(endptr == str + 6); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2044,7 +2044,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x1fffffffffffff); - g_assert(endptr == str + 16); + g_assert_true(endptr == str + 16); str = "9007199254740992"; /* 2^53 */ endptr = str; @@ -2052,7 +2052,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x20000000000000); - g_assert(endptr == str + 16); + g_assert_true(endptr == str + 16); str = "9007199254740993"; /* 2^53+1 */ endptr = str; @@ -2060,7 +2060,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x20000000000001); - g_assert(endptr == str + 16); + g_assert_true(endptr == str + 16); str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ endptr = str; @@ -2068,7 +2068,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0xfffffffffffff800); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); str = "18446744073709550591"; /* 0xfffffffffffffbff */ endptr = str; @@ -2076,7 +2076,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0xfffffffffffffbff); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); str = "18446744073709551615"; /* 0xffffffffffffffff */ endptr = str; @@ -2084,7 +2084,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0xffffffffffffffff); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); } static void test_qemu_strtosz_hex(void) @@ -2100,7 +2100,7 @@ static void test_qemu_strtosz_hex(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); str = "0xab"; endptr = str; @@ -2108,7 +2108,7 @@ static void test_qemu_strtosz_hex(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 171); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); str = "0xae"; endptr = str; @@ -2116,7 +2116,7 @@ static void test_qemu_strtosz_hex(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 174); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); } static void test_qemu_strtosz_units(void) @@ -2139,56 +2139,56 @@ static void test_qemu_strtosz_units(void) err = qemu_strtosz_MiB(none, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, MiB); - g_assert(endptr == none + 1); + g_assert_true(endptr == none + 1); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(b, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1); - g_assert(endptr == b + 2); + g_assert_true(endptr == b + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(k, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, KiB); - g_assert(endptr == k + 2); + g_assert_true(endptr == k + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(m, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, MiB); - g_assert(endptr == m + 2); + g_assert_true(endptr == m + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(g, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, GiB); - g_assert(endptr == g + 2); + g_assert_true(endptr == g + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(t, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, TiB); - g_assert(endptr == t + 2); + g_assert_true(endptr == t + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(p, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, PiB); - g_assert(endptr == p + 2); + g_assert_true(endptr == p + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(e, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, EiB); - g_assert(endptr == e + 2); + g_assert_true(endptr == e + 2); } static void test_qemu_strtosz_float(void) @@ -2204,7 +2204,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, EiB / 2); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); /* For convenience, a fraction of 0 is tolerated even on bytes */ str = "1.0B"; @@ -2213,7 +2213,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); /* An empty fraction is tolerated */ str = "1.k"; @@ -2222,7 +2222,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); /* For convenience, we permit values that are not byte-exact */ str = "12.345M"; @@ -2231,7 +2231,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); - g_assert(endptr == str + 7); + g_assert_true(endptr == str + 7); } static void test_qemu_strtosz_invalid(void) @@ -2246,35 +2246,35 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = " \t "; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "crap"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "inf"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "NaN"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* Fractional values require scale larger than bytes */ str = "1.1B"; @@ -2282,14 +2282,14 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "1.1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No floating point exponents */ str = "1.5e1k"; @@ -2297,14 +2297,14 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "1.5E+0k"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No hex fractions */ str = "0x1.8k"; @@ -2312,7 +2312,7 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No suffixes */ str = "0x18M"; @@ -2320,7 +2320,7 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No negative values */ str = "-0"; @@ -2328,14 +2328,14 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "-1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtosz_trailing(void) @@ -2351,7 +2351,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz_MiB(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123 * MiB); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2364,7 +2364,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 2); + g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2377,7 +2377,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2390,7 +2390,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 2); + g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2403,7 +2403,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2423,14 +2423,14 @@ static void test_qemu_strtosz_erange(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); str = "20E"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtosz_metric(void) @@ -2446,7 +2446,7 @@ static void test_qemu_strtosz_metric(void) err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 6); + g_assert_true(endptr == str + 6); str = "12.345M"; endptr = str; @@ -2454,7 +2454,7 @@ static void test_qemu_strtosz_metric(void) err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 7); + g_assert_true(endptr == str + 7); } static void test_freq_to_str(void) From 3b4790d4ec1f01a1d0575bda23aa08a2061d601c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 11 May 2023 21:10:16 -0500 Subject: [PATCH 014/412] test-cutils: Use g_assert_cmpuint where appropriate When debugging test failures, seeing unsigned values as large positive values rather than negative values matters (assuming glib 2.78+; given that I just fixed a bug in glib 2.76 [1] where g_assert_cmpuint displays signed instead of unsigned values). No impact when the test is passing, but using a consistent style will matter more in upcoming test additions. Also, some tests are better with cmphex. While at it, fix some spacing and minor typing issues spotted nearby. [1] https://gitlab.gnome.org/GNOME/glib/-/issues/2997 Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-3-eblake@redhat.com> --- tests/unit/test-cutils.c | 148 +++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 0202ac0d5b..38bd399020 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -39,7 +39,7 @@ static void test_parse_uint_null(void) r = parse_uint(NULL, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_null(endptr); } @@ -54,7 +54,7 @@ static void test_parse_uint_empty(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str); } @@ -69,7 +69,7 @@ static void test_parse_uint_whitespace(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str); } @@ -85,7 +85,7 @@ static void test_parse_uint_invalid(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str); } @@ -101,7 +101,7 @@ static void test_parse_uint_trailing(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); g_assert_true(endptr == str + 3); } @@ -116,7 +116,7 @@ static void test_parse_uint_correct(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); g_assert_true(endptr == str + strlen(str)); } @@ -131,7 +131,7 @@ static void test_parse_uint_octal(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 0123); + g_assert_cmpuint(i, ==, 0123); g_assert_true(endptr == str + strlen(str)); } @@ -146,7 +146,7 @@ static void test_parse_uint_decimal(void) r = parse_uint(str, &i, &endptr, 10); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); g_assert_true(endptr == str + strlen(str)); } @@ -162,7 +162,7 @@ static void test_parse_uint_llong_max(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); + g_assert_cmpuint(i, ==, (unsigned long long)LLONG_MAX + 1); g_assert_true(endptr == str + strlen(str)); g_free(str); @@ -179,7 +179,7 @@ static void test_parse_uint_overflow(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, ULLONG_MAX); + g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); } @@ -194,7 +194,7 @@ static void test_parse_uint_negative(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str + strlen(str)); } @@ -208,7 +208,7 @@ static void test_parse_uint_full_trailing(void) r = parse_uint_full(str, &i, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); } static void test_parse_uint_full_correct(void) @@ -220,7 +220,7 @@ static void test_parse_uint_full_correct(void) r = parse_uint_full(str, &i, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); } static void test_qemu_strtoi_correct(void) @@ -428,7 +428,7 @@ static void test_qemu_strtoi_underflow(void) int res = 999; int err; - err = qemu_strtoi(str, &endptr, 0, &res); + err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MIN); @@ -479,10 +479,10 @@ static void test_qemu_strtoi_full_null(void) static void test_qemu_strtoi_full_empty(void) { const char *str = ""; - int res = 999L; + int res = 999; int err; - err = qemu_strtoi(str, NULL, 0, &res); + err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } @@ -728,7 +728,7 @@ static void test_qemu_strtoui_underflow(void) unsigned int res = 999; int err; - err = qemu_strtoui(str, &endptr, 0, &res); + err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, (unsigned int)-1); @@ -1022,7 +1022,7 @@ static void test_qemu_strtol_underflow(void) long res = 999; int err; - err = qemu_strtol(str, &endptr, 0, &res); + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); @@ -1075,7 +1075,7 @@ static void test_qemu_strtol_full_empty(void) long res = 999L; int err; - err = qemu_strtol(str, NULL, 0, &res); + err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } @@ -1320,7 +1320,7 @@ static void test_qemu_strtoul_underflow(void) unsigned long res = 999; int err; - err = qemu_strtoul(str, &endptr, 0, &res); + err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, -1ul); @@ -1613,7 +1613,7 @@ static void test_qemu_strtoi64_underflow(void) int64_t res = 999; int err; - err = qemu_strtoi64(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MIN); @@ -1909,7 +1909,7 @@ static void test_qemu_strtou64_underflow(void) uint64_t res = 999; int err; - err = qemu_strtou64(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, -1ull); @@ -2012,7 +2012,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 1); /* Leading 0 gives decimal results, not octal */ @@ -2021,7 +2021,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 8); + g_assert_cmpuint(res, ==, 8); g_assert_true(endptr == str + 2); /* Leading space is ignored */ @@ -2030,20 +2030,20 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + g_assert_cmpuint(res, ==, 12345); g_assert_true(endptr == str + 6); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + g_assert_cmpuint(res, ==, 12345); str = "9007199254740991"; /* 2^53-1 */ endptr = str; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x1fffffffffffff); + g_assert_cmphex(res, ==, 0x1fffffffffffffULL); g_assert_true(endptr == str + 16); str = "9007199254740992"; /* 2^53 */ @@ -2051,7 +2051,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000000); + g_assert_cmphex(res, ==, 0x20000000000000ULL); g_assert_true(endptr == str + 16); str = "9007199254740993"; /* 2^53+1 */ @@ -2059,7 +2059,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000001); + g_assert_cmphex(res, ==, 0x20000000000001ULL); g_assert_true(endptr == str + 16); str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ @@ -2067,7 +2067,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffff800); + g_assert_cmphex(res, ==, 0xfffffffffffff800ULL); g_assert_true(endptr == str + 20); str = "18446744073709550591"; /* 0xfffffffffffffbff */ @@ -2075,7 +2075,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffffbff); + g_assert_cmphex(res, ==, 0xfffffffffffffbffULL); g_assert_true(endptr == str + 20); str = "18446744073709551615"; /* 0xffffffffffffffff */ @@ -2083,7 +2083,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xffffffffffffffff); + g_assert_cmphex(res, ==, 0xffffffffffffffffULL); g_assert_true(endptr == str + 20); } @@ -2099,7 +2099,7 @@ static void test_qemu_strtosz_hex(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 3); str = "0xab"; @@ -2107,7 +2107,7 @@ static void test_qemu_strtosz_hex(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 171); + g_assert_cmpuint(res, ==, 171); g_assert_true(endptr == str + 4); str = "0xae"; @@ -2115,7 +2115,7 @@ static void test_qemu_strtosz_hex(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 174); + g_assert_cmpuint(res, ==, 174); g_assert_true(endptr == str + 4); } @@ -2138,56 +2138,56 @@ static void test_qemu_strtosz_units(void) res = 0xbaadf00d; err = qemu_strtosz_MiB(none, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); + g_assert_cmpuint(res, ==, MiB); g_assert_true(endptr == none + 1); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(b, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); + g_assert_cmpuint(res, ==, 1); g_assert_true(endptr == b + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(k, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, KiB); + g_assert_cmpuint(res, ==, KiB); g_assert_true(endptr == k + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(m, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); + g_assert_cmpuint(res, ==, MiB); g_assert_true(endptr == m + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(g, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, GiB); + g_assert_cmpuint(res, ==, GiB); g_assert_true(endptr == g + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(t, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, TiB); + g_assert_cmpuint(res, ==, TiB); g_assert_true(endptr == t + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(p, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, PiB); + g_assert_cmpuint(res, ==, PiB); g_assert_true(endptr == p + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(e, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB); + g_assert_cmpuint(res, ==, EiB); g_assert_true(endptr == e + 2); } @@ -2203,7 +2203,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB / 2); + g_assert_cmpuint(res, ==, EiB / 2); g_assert_true(endptr == str + 4); /* For convenience, a fraction of 0 is tolerated even on bytes */ @@ -2212,7 +2212,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); + g_assert_cmpuint(res, ==, 1); g_assert_true(endptr == str + 4); /* An empty fraction is tolerated */ @@ -2221,7 +2221,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); + g_assert_cmpuint(res, ==, 1024); g_assert_true(endptr == str + 3); /* For convenience, we permit values that are not byte-exact */ @@ -2230,7 +2230,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); + g_assert_cmpuint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); g_assert_true(endptr == str + 7); } @@ -2245,35 +2245,35 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = " \t "; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "crap"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "inf"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "NaN"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* Fractional values require scale larger than bytes */ @@ -2281,14 +2281,14 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "1.1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No floating point exponents */ @@ -2296,14 +2296,14 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "1.5E+0k"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No hex fractions */ @@ -2311,7 +2311,7 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No suffixes */ @@ -2319,7 +2319,7 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No negative values */ @@ -2327,14 +2327,14 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "-1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); } @@ -2350,65 +2350,65 @@ static void test_qemu_strtosz_trailing(void) res = 0xbaadf00d; err = qemu_strtosz_MiB(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123 * MiB); + g_assert_cmpuint(res, ==, 123 * MiB); g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "1kiB"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); + g_assert_cmpuint(res, ==, 1024); g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "0x"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 1); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "0.NaN"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "123-45"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); } static void test_qemu_strtosz_erange(void) @@ -2422,14 +2422,14 @@ static void test_qemu_strtosz_erange(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str + 20); str = "20E"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str + 3); } @@ -2445,7 +2445,7 @@ static void test_qemu_strtosz_metric(void) res = 0xbaadf00d; err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); + g_assert_cmpuint(res, ==, 12345000); g_assert_true(endptr == str + 6); str = "12.345M"; @@ -2453,7 +2453,7 @@ static void test_qemu_strtosz_metric(void) res = 0xbaadf00d; err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); + g_assert_cmpuint(res, ==, 12345000); g_assert_true(endptr == str + 7); } From d326d03bcdf0751049ec0bfbe246a2e828008a13 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:25 -0500 Subject: [PATCH 015/412] test-cutils: Test integral qemu_strto* value on failures We are inconsistent on the contents of *value after a strto* parse failure. I found the following behaviors: - parse_uint() and parse_uint_full(), which document that *value is slammed to 0 on all EINVAL failures and 0 or UINT_MAX on ERANGE failures, and has unit tests for that (note that parse_uint requires non-NULL endptr, and does not fail with EINVAL for trailing junk) - qemu_strtosz(), which leaves *value untouched on all failures (both EINVAL and ERANGE), and has unit tests but not documentation for that - qemu_strtoi() and other integral friends, which document *value on ERANGE failures but is unspecified on EINVAL (other than implicitly by comparison to libc strto*); there, *value is untouched for NULL string, slammed to 0 on no conversion, and left at the prefix value on NULL endptr; unit tests do not consistently check the value - qemu_strtod(), which documents *value on ERANGE failures but is unspecified on EINVAL; there, *value is untouched for NULL string, slammed to 0.0 for no conversion, and left at the prefix value on NULL endptr; there are no unit tests (other than indirectly through qemu_strtosz) - qemu_strtod_finite(), which documents *value on ERANGE failures but is unspecified on EINVAL; there, *value is left at the prefix for 'inf' or 'nan' and untouched in all other cases; there are no unit tests (other than indirectly through qemu_strtosz) Upcoming patches will change behaviors for consistency, but it's best to first have more unit test coverage to see the impact of those changes. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-4-eblake@redhat.com> --- tests/unit/test-cutils.c | 58 +++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 38bd399020..1eeaf21ae2 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -248,6 +248,7 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -262,6 +263,7 @@ static void test_qemu_strtoi_empty(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -276,6 +278,7 @@ static void test_qemu_strtoi_whitespace(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -290,6 +293,7 @@ static void test_qemu_strtoi_invalid(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -473,6 +477,7 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -485,6 +490,7 @@ static void test_qemu_strtoi_full_empty(void) err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi_full_negative(void) @@ -502,18 +508,19 @@ static void test_qemu_strtoi_full_negative(void) static void test_qemu_strtoi_full_trailing(void) { const char *str = "123xxx"; - int res; + int res = 999; int err; err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtoi_full_max(void) { char *str = g_strdup_printf("%d", INT_MAX); - int res; + int res = 999; int err; err = qemu_strtoi(str, NULL, 0, &res); @@ -548,6 +555,7 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); g_assert_null(endptr); } @@ -562,6 +570,7 @@ static void test_qemu_strtoui_empty(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -576,6 +585,7 @@ static void test_qemu_strtoui_whitespace(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -590,6 +600,7 @@ static void test_qemu_strtoui_invalid(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -771,6 +782,7 @@ static void test_qemu_strtoui_full_null(void) err = qemu_strtoui(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); } static void test_qemu_strtoui_full_empty(void) @@ -782,7 +794,9 @@ static void test_qemu_strtoui_full_empty(void) err = qemu_strtoui(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } + static void test_qemu_strtoui_full_negative(void) { const char *str = " \t -321"; @@ -797,12 +811,13 @@ static void test_qemu_strtoui_full_negative(void) static void test_qemu_strtoui_full_trailing(void) { const char *str = "123xxx"; - unsigned int res; + unsigned int res = 999; int err; err = qemu_strtoui(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoui_full_max(void) @@ -843,6 +858,7 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -857,6 +873,7 @@ static void test_qemu_strtol_empty(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -871,6 +888,7 @@ static void test_qemu_strtol_whitespace(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -885,6 +903,7 @@ static void test_qemu_strtol_invalid(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1066,6 +1085,7 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -1078,6 +1098,7 @@ static void test_qemu_strtol_full_empty(void) err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtol_full_negative(void) @@ -1095,18 +1116,19 @@ static void test_qemu_strtol_full_negative(void) static void test_qemu_strtol_full_trailing(void) { const char *str = "123xxx"; - long res; + long res = 999; int err; err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtol_full_max(void) { char *str = g_strdup_printf("%ld", LONG_MAX); - long res; + long res = 999; int err; err = qemu_strtol(str, NULL, 0, &res); @@ -1141,6 +1163,7 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); g_assert_null(endptr); } @@ -1155,6 +1178,7 @@ static void test_qemu_strtoul_empty(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1169,6 +1193,7 @@ static void test_qemu_strtoul_whitespace(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1183,6 +1208,7 @@ static void test_qemu_strtoul_invalid(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1362,6 +1388,7 @@ static void test_qemu_strtoul_full_null(void) err = qemu_strtoul(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); } static void test_qemu_strtoul_full_empty(void) @@ -1373,7 +1400,9 @@ static void test_qemu_strtoul_full_empty(void) err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } + static void test_qemu_strtoul_full_negative(void) { const char *str = " \t -321"; @@ -1388,12 +1417,13 @@ static void test_qemu_strtoul_full_negative(void) static void test_qemu_strtoul_full_trailing(void) { const char *str = "123xxx"; - unsigned long res; + unsigned long res = 999; int err; err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoul_full_max(void) @@ -1434,6 +1464,7 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -1448,6 +1479,7 @@ static void test_qemu_strtoi64_empty(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1462,6 +1494,7 @@ static void test_qemu_strtoi64_whitespace(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1476,6 +1509,7 @@ static void test_qemu_strtoi64_invalid(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1655,6 +1689,7 @@ static void test_qemu_strtoi64_full_null(void) err = qemu_strtoi64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); } static void test_qemu_strtoi64_full_empty(void) @@ -1666,6 +1701,7 @@ static void test_qemu_strtoi64_full_empty(void) err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi64_full_negative(void) @@ -1689,13 +1725,14 @@ static void test_qemu_strtoi64_full_trailing(void) err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtoi64_full_max(void) { char *str = g_strdup_printf("%lld", LLONG_MAX); - int64_t res; + int64_t res = 999; int err; err = qemu_strtoi64(str, NULL, 0, &res); @@ -1730,6 +1767,7 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); g_assert_null(endptr); } @@ -1744,6 +1782,7 @@ static void test_qemu_strtou64_empty(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1758,6 +1797,7 @@ static void test_qemu_strtou64_whitespace(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1772,6 +1812,7 @@ static void test_qemu_strtou64_invalid(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1951,6 +1992,7 @@ static void test_qemu_strtou64_full_null(void) err = qemu_strtou64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); } static void test_qemu_strtou64_full_empty(void) @@ -1962,6 +2004,7 @@ static void test_qemu_strtou64_full_empty(void) err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtou64_full_negative(void) @@ -1985,6 +2028,7 @@ static void test_qemu_strtou64_full_trailing(void) err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 18446744073709551614ULL); } static void test_qemu_strtou64_full_max(void) From 3069522bb9db4586262cbbbf447c5c5211d5921d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:26 -0500 Subject: [PATCH 016/412] test-cutils: Test more integer corner cases We have quite a few undertested and underdocumented integer parsing corner cases. To ensure that any changes we make in the code are intentional rather than accidental semantic changes, it is time to add more unit tests of existing behavior. In particular, this demonstrates that parse_uint() and qemu_strtou64() behave differently. For "-0", it's hard to argue why parse_uint needs to reject it (it's not a negative integer), but the documentation sort of mentions it; but it is intentional that all other negative values are treated as ERANGE with value 0 (compared to qemu_strtou64() treating "-2" as success and UINT64_MAX-1, for example). Also, when mixing overflow/underflow with a check for no trailing junk, parse_uint_full favors ERANGE over EINVAL, while qemu_strto[iu]* favor EINVAL. This behavior is outside the C standard, so we can pick whatever we want, but it would be nice to be consistent. Note that C requires that "9223372036854775808" fail strtoll() with ERANGE/INT64_MAX, but "-9223372036854775808" pass with INT64_MIN; we weren't testing this. For strtol(), the behavior depends on whether long is 32- or 64-bits (the cutoff point either being the same as strtoll() or at "-2147483648"). Meanwhile, C is clear that "-18446744073709551615" pass stroull() (but not strtoll) with value 1, even though we want it to fail parse_uint(). And although qemu_strtoui() has no C counterpart, it makes more sense if we design it like 32-bit strtoul() (that is, where "-4294967296" be an alternate acceptable spelling for "1", but "-0xffffffff00000001" should be treated as overflow and return 0xffffffff rather than 1). We aren't there yet, so some of the tests added in this patch have FIXME comments. However, note that C2x will (likely) be adding a SILENT semantic change, where C17 strtol("0b1", &ep, 2) returns 0 with ep="b1", but C2x will have it return 1 with ep="". I did not feel like adding testing for those corner cases, in part because the next version of C is not standard and libc support for binary parsing is not yet wide-spread (as of this patch, glibc.git still misparses bare "0b": https://sourceware.org/bugzilla/show_bug.cgi?id=30371). Message-Id: <20230522190441.64278-5-eblake@redhat.com> [eblake: fix a few typos spotted by Hanna] Reviewed-by: Hanna Czenczek [eblake: fix typo on platforms with 32-bit long] Signed-off-by: Eric Blake --- tests/unit/test-cutils.c | 959 +++++++++++++++++++++++++++++++++++---- 1 file changed, 879 insertions(+), 80 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 1eeaf21ae2..30cb5f122c 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -150,7 +150,6 @@ static void test_parse_uint_decimal(void) g_assert_true(endptr == str + strlen(str)); } - static void test_parse_uint_llong_max(void) { unsigned long long i = 999; @@ -168,27 +167,87 @@ static void test_parse_uint_llong_max(void) g_free(str); } -static void test_parse_uint_overflow(void) +static void test_parse_uint_max(void) { unsigned long long i = 999; char f = 'X'; char *endptr = &f; - const char *str = "99999999999999999999999999999999999999"; + char *str = g_strdup_printf("%llu", ULLONG_MAX); int r; r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, 0); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + g_free(str); +} + +static void test_parse_uint_overflow(void) +{ + unsigned long long i; + char f = 'X'; + char *endptr; + const char *str; + int r; + + i = 999; + endptr = &f; + str = "99999999999999999999999999999999999999"; + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = &f; + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = &f; + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_negative(void) +{ + unsigned long long i; + char f = 'X'; + char *endptr; + const char *str; + int r; + + i = 999; + endptr = &f; + str = " \t -321"; + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = &f; + str = "-0xffffffff00000001"; + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_parse_uint_negzero(void) { unsigned long long i = 999; char f = 'X'; char *endptr = &f; - const char *str = " \t -321"; + const char *str = " -0"; int r; r = parse_uint(str, &i, &endptr, 0); @@ -198,7 +257,6 @@ static void test_parse_uint_negative(void) g_assert_true(endptr == str + strlen(str)); } - static void test_parse_uint_full_trailing(void) { unsigned long long i = 999; @@ -223,6 +281,19 @@ static void test_parse_uint_full_correct(void) g_assert_cmpuint(i, ==, 123); } +static void test_parse_uint_full_erange_junk(void) +{ + /* FIXME - inconsistent with qemu_strto* which favors EINVAL */ + unsigned long long i = 999; + const char *str = "-2junk"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, -ERANGE /* FIXME -EINVAL */); + g_assert_cmpuint(i, ==, 0); +} + static void test_qemu_strtoi_correct(void) { const char *str = "12345 foo"; @@ -410,39 +481,55 @@ static void test_qemu_strtoi_max(void) static void test_qemu_strtoi_overflow(void) { - char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; + const char *str; + const char *endptr; + int res; int err; + str = "2147483648"; /* INT_MAX + 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi_underflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; + str = "0x7fffffffffffffff"; /* LLONG_MAX */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, INT_MIN); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); } -static void test_qemu_strtoi_negative(void) +static void test_qemu_strtoi_min(void) { - const char *str = " \t -321"; + char *str = g_strdup_printf("%d", INT_MIN); char f = 'X'; const char *endptr = &f; int res = 999; @@ -450,9 +537,105 @@ static void test_qemu_strtoi_negative(void) err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_underflow(void) +{ + const char *str; + const char *endptr; + int res; + int err; + + str = "-2147483649"; /* INT_MIN - 1ll */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x7fffffffffffffff"; /* -LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-18446744073709551615"; /* -UINT64_MAX (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_negative(void) +{ + const char *str; + const char *endptr; + int res; + int err; + + str = " \t -321"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); g_assert_true(endptr == str + strlen(str)); + + str = "-2147483648"; /* INT_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_full_correct(void) @@ -505,6 +688,18 @@ static void test_qemu_strtoi_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtoi_full_negzero(void) +{ + const char *str = " -0"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtoi_full_trailing(void) { const char *str = "123xxx"; @@ -530,6 +725,19 @@ static void test_qemu_strtoi_full_max(void) g_free(str); } +static void test_qemu_strtoi_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-9999999999junk"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, INT_MIN); +} + static void test_qemu_strtoui_correct(void) { const char *str = "12345 foo"; @@ -699,6 +907,22 @@ static void test_qemu_strtoui_hex(void) g_assert_true(endptr == str + 1); } +static void test_qemu_strtoui_wrap(void) +{ + /* FIXME - wraparound should be consistent with 32-bit strtoul */ + const char *str = "-4294967295"; /* 1 mod 2^32 */ + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE /* FIXME 0 */); + g_assert_cmphex(res, ==, UINT_MAX /* FIXME 1 */); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoui_max(void) { char *str = g_strdup_printf("%u", UINT_MAX); @@ -717,34 +941,116 @@ static void test_qemu_strtoui_max(void) static void test_qemu_strtoui_overflow(void) { - char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; + const char *str; + const char *endptr; + unsigned int res; int err; + str = "4294967296"; /* UINT_MAX + 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, UINT_MAX); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x7fffffffffffffff"; /* LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0xffffffff00000001"; /* ULLONG_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + /* FIXME - overflow should be consistent with 32-bit strtoul */ + str = "0xfffffffffffffffe"; /* ULLONG_MAX - 1 (not UINT_MAX - 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); + g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); } static void test_qemu_strtoui_underflow(void) { - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; + const char *str; + const char *endptr; + unsigned int res; int err; + str = "-4294967296"; /* -(long long)UINT_MAX - 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, (unsigned int)-1); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + /* FIXME - overflow should be consistent with 32-bit strtoul */ + str = "-18446744073709551615"; /* -UINT64_MAX (not -(-1)) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); + g_assert_cmpuint(res, ==, 1 /* FIXME UINT_MAX */); + g_assert_true(endptr == str + strlen(str)); + + str = "-0xffffffff00000002"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); + g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); } static void test_qemu_strtoui_negative(void) @@ -762,6 +1068,21 @@ static void test_qemu_strtoui_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtoui_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoui_full_correct(void) { const char *str = "123"; @@ -808,6 +1129,17 @@ static void test_qemu_strtoui_full_negative(void) g_assert_cmpuint(res, ==, (unsigned int)-321); } +static void test_qemu_strtoui_full_negzero(void) +{ + const char *str = " -0"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtoui_full_trailing(void) { const char *str = "123xxx"; @@ -833,6 +1165,19 @@ static void test_qemu_strtoui_full_max(void) g_free(str); } +static void test_qemu_strtoui_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-9999999999junk"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, UINT_MAX); +} + static void test_qemu_strtol_correct(void) { const char *str = "12345 foo"; @@ -1020,22 +1365,50 @@ static void test_qemu_strtol_max(void) static void test_qemu_strtol_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - long res = 999; + const char *str; + const char *endptr; + long res; int err; + /* 1 more than LONG_MAX */ + str = LONG_MAX == INT_MAX ? "2147483648" : "9223372036854775808"; + endptr = "somewhere"; + res = 999; err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + if (LONG_MAX == INT_MAX) { + str = "0xffffffff00000001"; /* ULLONG_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MAX); g_assert_true(endptr == str + strlen(str)); } -static void test_qemu_strtol_underflow(void) +static void test_qemu_strtol_min(void) { - const char *str = "-99999999999999999999999999999999999999999999"; + char *str = g_strdup_printf("%ld", LONG_MIN); char f = 'X'; const char *endptr = &f; long res = 999; @@ -1043,6 +1416,50 @@ static void test_qemu_strtol_underflow(void) err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtol_underflow(void) +{ + const char *str; + const char *endptr; + long res; + int err; + + /* 1 less than LONG_MIN */ + str = LONG_MIN == INT_MIN ? "-2147483649" : "-9223372036854775809"; + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "-18446744073709551615"; /* -UINT64_MAX (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + } + + str = "-0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); g_assert_true(endptr == str + strlen(str)); @@ -1063,6 +1480,21 @@ static void test_qemu_strtol_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtol_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtol_full_correct(void) { const char *str = "123"; @@ -1113,6 +1545,18 @@ static void test_qemu_strtol_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtol_full_negzero(void) +{ + const char *str = " -0"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtol_full_trailing(void) { const char *str = "123xxx"; @@ -1138,6 +1582,19 @@ static void test_qemu_strtol_full_max(void) g_free(str); } +static void test_qemu_strtol_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, LONG_MIN); +} + static void test_qemu_strtoul_correct(void) { const char *str = "12345 foo"; @@ -1307,6 +1764,23 @@ static void test_qemu_strtoul_hex(void) g_assert_true(endptr == str + 1); } +static void test_qemu_strtoul_wrap(void) +{ + const char *str; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + /* 1 mod 2^(sizeof(long)*8) */ + str = LONG_MAX == INT_MAX ? "-4294967295" : "-18446744073709551615"; + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoul_max(void) { char *str = g_strdup_printf("%lu", ULONG_MAX); @@ -1325,31 +1799,87 @@ static void test_qemu_strtoul_max(void) static void test_qemu_strtoul_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; + const char *str; + const char *endptr; + unsigned long res; int err; + /* 1 more than ULONG_MAX */ + str = ULONG_MAX == UINT_MAX ? "4294967296" : "18446744073709551616"; + endptr = "somewhere"; + res = 999; err = qemu_strtoul(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULONG_MAX); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "0xffffffff00000001"; /* UINT64_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; + const char *str; + const char *endptr; + unsigned long res; int err; + /* 1 less than -ULONG_MAX */ + str = ULONG_MAX == UINT_MAX ? "-4294967296" : "-18446744073709551616"; + endptr = "somewhere"; + res = 999; err = qemu_strtoul(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, -1ul); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "-0xffffffff00000002"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "-0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); g_assert_true(endptr == str + strlen(str)); } @@ -1368,6 +1898,21 @@ static void test_qemu_strtoul_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtoul_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoul_full_correct(void) { const char *str = "123"; @@ -1414,6 +1959,17 @@ static void test_qemu_strtoul_full_negative(void) g_assert_cmpuint(res, ==, -321ul); } +static void test_qemu_strtoul_full_negzero(void) +{ + const char *str = " -0"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtoul_full_trailing(void) { const char *str = "123xxx"; @@ -1439,6 +1995,19 @@ static void test_qemu_strtoul_full_max(void) g_free(str); } +static void test_qemu_strtoul_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, ULONG_MAX); +} + static void test_qemu_strtoi64_correct(void) { const char *str = "12345 foo"; @@ -1626,7 +2195,39 @@ static void test_qemu_strtoi64_max(void) static void test_qemu_strtoi64_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; + const char *str; + const char *endptr; + int64_t res; + int err; + + str = "9223372036854775808"; /* 1 more than INT64_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_min(void) +{ + char *str = g_strdup_printf("%lld", LLONG_MIN); char f = 'X'; const char *endptr = &f; int64_t res = 999; @@ -1634,23 +2235,41 @@ static void test_qemu_strtoi64_overflow(void) err = qemu_strtoi64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MAX); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MIN); g_assert_true(endptr == str + strlen(str)); + g_free(str); } static void test_qemu_strtoi64_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; + const char *str; + const char *endptr; + int64_t res; int err; + str = "-9223372036854775809"; /* 1 less than INT64_MIN */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MIN); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MIN); g_assert_true(endptr == str + strlen(str)); } @@ -1669,6 +2288,21 @@ static void test_qemu_strtoi64_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtoi64_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoi64_full_correct(void) { const char *str = "123"; @@ -1716,6 +2350,18 @@ static void test_qemu_strtoi64_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtoi64_full_negzero(void) +{ + const char *str = " -0"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtoi64_full_trailing(void) { const char *str = "123xxx"; @@ -1742,6 +2388,19 @@ static void test_qemu_strtoi64_full_max(void) g_free(str); } +static void test_qemu_strtoi64_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, INT64_MIN); +} + static void test_qemu_strtou64_correct(void) { const char *str = "12345 foo"; @@ -1911,6 +2570,21 @@ static void test_qemu_strtou64_hex(void) g_assert_true(endptr == str + 1); } +static void test_qemu_strtou64_wrap(void) +{ + const char *str = "-18446744073709551615"; /* 1 mod 2^64 */ + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtou64_max(void) { char *str = g_strdup_printf("%llu", ULLONG_MAX); @@ -1929,31 +2603,65 @@ static void test_qemu_strtou64_max(void) static void test_qemu_strtou64_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; + const char *str; + const char *endptr; + uint64_t res; int err; + str = "18446744073709551616"; /* 1 more than UINT64_MAX */ + endptr = "somewhere"; + res = 999; err = qemu_strtou64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULLONG_MAX); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; + const char *str; + const char *endptr; + uint64_t res; int err; + str = "-99999999999999999999999999999999999999999999"; + endptr = "somewhere"; + res = 999; err = qemu_strtou64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, -1ull); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); g_assert_true(endptr == str + strlen(str)); } @@ -1972,6 +2680,21 @@ static void test_qemu_strtou64_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtou64_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtou64_full_correct(void) { const char *str = "18446744073709551614"; @@ -2019,6 +2742,18 @@ static void test_qemu_strtou64_full_negative(void) g_assert_cmpuint(res, ==, -321ull); } +static void test_qemu_strtou64_full_negzero(void) +{ + const char *str = " -0"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtou64_full_trailing(void) { const char *str = "18446744073709551614xxxxxx"; @@ -2044,6 +2779,19 @@ static void test_qemu_strtou64_full_max(void) g_free(str); } +static void test_qemu_strtou64_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, UINT64_MAX); +} + static void test_qemu_strtosz_simple(void) { const char *str; @@ -2585,12 +3333,16 @@ int main(int argc, char **argv) g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/max", test_parse_uint_max); g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint/negzero", test_parse_uint_negzero); g_test_add_func("/cutils/parse_uint_full/trailing", test_parse_uint_full_trailing); g_test_add_func("/cutils/parse_uint_full/correct", test_parse_uint_full_correct); + g_test_add_func("/cutils/parse_uint_full/erange_junk", + test_parse_uint_full_erange_junk); /* qemu_strtoi() tests */ g_test_add_func("/cutils/qemu_strtoi/correct", @@ -2615,10 +3367,14 @@ int main(int argc, char **argv) test_qemu_strtoi_max); g_test_add_func("/cutils/qemu_strtoi/overflow", test_qemu_strtoi_overflow); + g_test_add_func("/cutils/qemu_strtoi/min", + test_qemu_strtoi_min); g_test_add_func("/cutils/qemu_strtoi/underflow", test_qemu_strtoi_underflow); g_test_add_func("/cutils/qemu_strtoi/negative", test_qemu_strtoi_negative); + g_test_add_func("/cutils/qemu_strtoi/negzero", + test_qemu_strtoi_negzero); g_test_add_func("/cutils/qemu_strtoi_full/correct", test_qemu_strtoi_full_correct); g_test_add_func("/cutils/qemu_strtoi_full/null", @@ -2627,10 +3383,14 @@ int main(int argc, char **argv) test_qemu_strtoi_full_empty); g_test_add_func("/cutils/qemu_strtoi_full/negative", test_qemu_strtoi_full_negative); + g_test_add_func("/cutils/qemu_strtoi_full/negzero", + test_qemu_strtoi_full_negzero); g_test_add_func("/cutils/qemu_strtoi_full/trailing", test_qemu_strtoi_full_trailing); g_test_add_func("/cutils/qemu_strtoi_full/max", test_qemu_strtoi_full_max); + g_test_add_func("/cutils/qemu_strtoi_full/erange_junk", + test_qemu_strtoi_full_erange_junk); /* qemu_strtoui() tests */ g_test_add_func("/cutils/qemu_strtoui/correct", @@ -2651,6 +3411,8 @@ int main(int argc, char **argv) test_qemu_strtoui_decimal); g_test_add_func("/cutils/qemu_strtoui/hex", test_qemu_strtoui_hex); + g_test_add_func("/cutils/qemu_strtoui/wrap", + test_qemu_strtoui_wrap); g_test_add_func("/cutils/qemu_strtoui/max", test_qemu_strtoui_max); g_test_add_func("/cutils/qemu_strtoui/overflow", @@ -2659,6 +3421,8 @@ int main(int argc, char **argv) test_qemu_strtoui_underflow); g_test_add_func("/cutils/qemu_strtoui/negative", test_qemu_strtoui_negative); + g_test_add_func("/cutils/qemu_strtoui/negzero", + test_qemu_strtoui_negzero); g_test_add_func("/cutils/qemu_strtoui_full/correct", test_qemu_strtoui_full_correct); g_test_add_func("/cutils/qemu_strtoui_full/null", @@ -2667,10 +3431,14 @@ int main(int argc, char **argv) test_qemu_strtoui_full_empty); g_test_add_func("/cutils/qemu_strtoui_full/negative", test_qemu_strtoui_full_negative); + g_test_add_func("/cutils/qemu_strtoui_full/negzero", + test_qemu_strtoui_full_negzero); g_test_add_func("/cutils/qemu_strtoui_full/trailing", test_qemu_strtoui_full_trailing); g_test_add_func("/cutils/qemu_strtoui_full/max", test_qemu_strtoui_full_max); + g_test_add_func("/cutils/qemu_strtoui_full/erange_junk", + test_qemu_strtoui_full_erange_junk); /* qemu_strtol() tests */ g_test_add_func("/cutils/qemu_strtol/correct", @@ -2695,10 +3463,14 @@ int main(int argc, char **argv) test_qemu_strtol_max); g_test_add_func("/cutils/qemu_strtol/overflow", test_qemu_strtol_overflow); + g_test_add_func("/cutils/qemu_strtol/min", + test_qemu_strtol_min); g_test_add_func("/cutils/qemu_strtol/underflow", test_qemu_strtol_underflow); g_test_add_func("/cutils/qemu_strtol/negative", test_qemu_strtol_negative); + g_test_add_func("/cutils/qemu_strtol/negzero", + test_qemu_strtol_negzero); g_test_add_func("/cutils/qemu_strtol_full/correct", test_qemu_strtol_full_correct); g_test_add_func("/cutils/qemu_strtol_full/null", @@ -2707,10 +3479,14 @@ int main(int argc, char **argv) test_qemu_strtol_full_empty); g_test_add_func("/cutils/qemu_strtol_full/negative", test_qemu_strtol_full_negative); + g_test_add_func("/cutils/qemu_strtol_full/negzero", + test_qemu_strtol_full_negzero); g_test_add_func("/cutils/qemu_strtol_full/trailing", test_qemu_strtol_full_trailing); g_test_add_func("/cutils/qemu_strtol_full/max", test_qemu_strtol_full_max); + g_test_add_func("/cutils/qemu_strtol_full/erange_junk", + test_qemu_strtol_full_erange_junk); /* qemu_strtoul() tests */ g_test_add_func("/cutils/qemu_strtoul/correct", @@ -2731,6 +3507,8 @@ int main(int argc, char **argv) test_qemu_strtoul_decimal); g_test_add_func("/cutils/qemu_strtoul/hex", test_qemu_strtoul_hex); + g_test_add_func("/cutils/qemu_strtoul/wrap", + test_qemu_strtoul_wrap); g_test_add_func("/cutils/qemu_strtoul/max", test_qemu_strtoul_max); g_test_add_func("/cutils/qemu_strtoul/overflow", @@ -2739,6 +3517,8 @@ int main(int argc, char **argv) test_qemu_strtoul_underflow); g_test_add_func("/cutils/qemu_strtoul/negative", test_qemu_strtoul_negative); + g_test_add_func("/cutils/qemu_strtoul/negzero", + test_qemu_strtoul_negzero); g_test_add_func("/cutils/qemu_strtoul_full/correct", test_qemu_strtoul_full_correct); g_test_add_func("/cutils/qemu_strtoul_full/null", @@ -2747,10 +3527,14 @@ int main(int argc, char **argv) test_qemu_strtoul_full_empty); g_test_add_func("/cutils/qemu_strtoul_full/negative", test_qemu_strtoul_full_negative); + g_test_add_func("/cutils/qemu_strtoul_full/negzero", + test_qemu_strtoul_full_negzero); g_test_add_func("/cutils/qemu_strtoul_full/trailing", test_qemu_strtoul_full_trailing); g_test_add_func("/cutils/qemu_strtoul_full/max", test_qemu_strtoul_full_max); + g_test_add_func("/cutils/qemu_strtoul_full/erange_junk", + test_qemu_strtoul_full_erange_junk); /* qemu_strtoi64() tests */ g_test_add_func("/cutils/qemu_strtoi64/correct", @@ -2761,8 +3545,7 @@ int main(int argc, char **argv) test_qemu_strtoi64_empty); g_test_add_func("/cutils/qemu_strtoi64/whitespace", test_qemu_strtoi64_whitespace); - g_test_add_func("/cutils/qemu_strtoi64/invalid" - , + g_test_add_func("/cutils/qemu_strtoi64/invalid", test_qemu_strtoi64_invalid); g_test_add_func("/cutils/qemu_strtoi64/trailing", test_qemu_strtoi64_trailing); @@ -2776,10 +3559,14 @@ int main(int argc, char **argv) test_qemu_strtoi64_max); g_test_add_func("/cutils/qemu_strtoi64/overflow", test_qemu_strtoi64_overflow); + g_test_add_func("/cutils/qemu_strtoi64/min", + test_qemu_strtoi64_min); g_test_add_func("/cutils/qemu_strtoi64/underflow", test_qemu_strtoi64_underflow); g_test_add_func("/cutils/qemu_strtoi64/negative", test_qemu_strtoi64_negative); + g_test_add_func("/cutils/qemu_strtoi64/negzero", + test_qemu_strtoi64_negzero); g_test_add_func("/cutils/qemu_strtoi64_full/correct", test_qemu_strtoi64_full_correct); g_test_add_func("/cutils/qemu_strtoi64_full/null", @@ -2788,10 +3575,14 @@ int main(int argc, char **argv) test_qemu_strtoi64_full_empty); g_test_add_func("/cutils/qemu_strtoi64_full/negative", test_qemu_strtoi64_full_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/negzero", + test_qemu_strtoi64_full_negzero); g_test_add_func("/cutils/qemu_strtoi64_full/trailing", test_qemu_strtoi64_full_trailing); g_test_add_func("/cutils/qemu_strtoi64_full/max", test_qemu_strtoi64_full_max); + g_test_add_func("/cutils/qemu_strtoi64_full/erange_junk", + test_qemu_strtoi64_full_erange_junk); /* qemu_strtou64() tests */ g_test_add_func("/cutils/qemu_strtou64/correct", @@ -2812,6 +3603,8 @@ int main(int argc, char **argv) test_qemu_strtou64_decimal); g_test_add_func("/cutils/qemu_strtou64/hex", test_qemu_strtou64_hex); + g_test_add_func("/cutils/qemu_strtou64/wrap", + test_qemu_strtou64_wrap); g_test_add_func("/cutils/qemu_strtou64/max", test_qemu_strtou64_max); g_test_add_func("/cutils/qemu_strtou64/overflow", @@ -2820,6 +3613,8 @@ int main(int argc, char **argv) test_qemu_strtou64_underflow); g_test_add_func("/cutils/qemu_strtou64/negative", test_qemu_strtou64_negative); + g_test_add_func("/cutils/qemu_strtou64/negzero", + test_qemu_strtou64_negzero); g_test_add_func("/cutils/qemu_strtou64_full/correct", test_qemu_strtou64_full_correct); g_test_add_func("/cutils/qemu_strtou64_full/null", @@ -2828,10 +3623,14 @@ int main(int argc, char **argv) test_qemu_strtou64_full_empty); g_test_add_func("/cutils/qemu_strtou64_full/negative", test_qemu_strtou64_full_negative); + g_test_add_func("/cutils/qemu_strtou64_full/negzero", + test_qemu_strtou64_full_negzero); g_test_add_func("/cutils/qemu_strtou64_full/trailing", test_qemu_strtou64_full_trailing); g_test_add_func("/cutils/qemu_strtou64_full/max", test_qemu_strtou64_full_max); + g_test_add_func("/cutils/qemu_strtou64_full/erange_junk", + test_qemu_strtou64_full_erange_junk); g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); From 56ddafde3f68b27123281dde3f48b5e945c86b48 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:27 -0500 Subject: [PATCH 017/412] cutils: Fix wraparound parsing in qemu_strtoui While we were matching 32-bit strtol in qemu_strtoi, our use of a 64-bit parse was leaking through for some inaccurate answers in qemu_strtoui in comparison to a 32-bit strtoul (see the unit test for examples). The comment for that function even described what we have to do for a correct parse, but didn't implement it correctly: since strtoull checks for overflow against the wrong values and then negates, we have to temporarily undo negation before checking for overflow against our desired value. Our int wrappers would be a lot easier to write if libc had a guaranteed 32-bit parser even on platforms with 64-bit long. Whether we parse C2x binary strings like "0b1000" is currently up to what libc does; our unit tests intentionally don't cover that at the moment, though. Fixes: 473a2a331e ("cutils: add qemu_strtoi & qemu_strtoui parsers for int/unsigned int types", v2.12.0) Signed-off-by: Eric Blake CC: qemu-stable@nongnu.org Message-Id: <20230522190441.64278-6-eblake@redhat.com> Reviewed-by: Hanna Czenczek --- tests/unit/test-cutils.c | 20 +++++++++----------- util/cutils.c | 25 +++++++++++++++++++------ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 30cb5f122c..8d2e057bda 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -909,7 +909,7 @@ static void test_qemu_strtoui_hex(void) static void test_qemu_strtoui_wrap(void) { - /* FIXME - wraparound should be consistent with 32-bit strtoul */ + /* wraparound is consistent with 32-bit strtoul */ const char *str = "-4294967295"; /* 1 mod 2^32 */ char f = 'X'; const char *endptr = &f; @@ -918,8 +918,8 @@ static void test_qemu_strtoui_wrap(void) err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE /* FIXME 0 */); - g_assert_cmphex(res, ==, UINT_MAX /* FIXME 1 */); + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 1); g_assert_true(endptr == str + strlen(str)); } @@ -978,13 +978,12 @@ static void test_qemu_strtoui_overflow(void) g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - /* FIXME - overflow should be consistent with 32-bit strtoul */ str = "0xfffffffffffffffe"; /* ULLONG_MAX - 1 (not UINT_MAX - 1) */ endptr = "somewhere"; res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); - g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ @@ -1019,21 +1018,20 @@ static void test_qemu_strtoui_underflow(void) g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - /* FIXME - overflow should be consistent with 32-bit strtoul */ str = "-18446744073709551615"; /* -UINT64_MAX (not -(-1)) */ endptr = "somewhere"; res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); - g_assert_cmpuint(res, ==, 1 /* FIXME UINT_MAX */); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); str = "-0xffffffff00000002"; endptr = "somewhere"; res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); - g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ diff --git a/util/cutils.c b/util/cutils.c index 5887e74414..9b6ce9179c 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -391,6 +391,9 @@ static int check_strtox_error(const char *nptr, char *ep, * and return -ERANGE. * * Else store the converted value in @result, and return zero. + * + * This matches the behavior of strtol() on 32-bit platforms, even on + * platforms where long is 64-bits. */ int qemu_strtoi(const char *nptr, const char **endptr, int base, int *result) @@ -443,13 +446,15 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, * * Note that a number with a leading minus sign gets converted without * the minus sign, checked for overflow (see above), then negated (in - * @result's type). This is exactly how strtoul() works. + * @result's type). This matches the behavior of strtoul() on 32-bit + * platforms, even on platforms where long is 64-bits. */ int qemu_strtoui(const char *nptr, const char **endptr, int base, unsigned int *result) { char *ep; - long long lresult; + unsigned long long lresult; + bool neg; assert((unsigned) base <= 36 && base != 1); if (!nptr) { @@ -466,14 +471,22 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, if (errno == ERANGE) { *result = -1; } else { + /* + * Note that platforms with 32-bit strtoul only accept input + * in the range [-4294967295, 4294967295]; but we used 64-bit + * strtoull which wraps -18446744073709551615 to 1 instead of + * declaring overflow. So we must check if '-' was parsed, + * and if so, undo the negation before doing our bounds check. + */ + neg = memchr(nptr, '-', ep - nptr) != NULL; + if (neg) { + lresult = -lresult; + } if (lresult > UINT_MAX) { *result = UINT_MAX; errno = ERANGE; - } else if (lresult < INT_MIN) { - *result = UINT_MAX; - errno = ERANGE; } else { - *result = lresult; + *result = neg ? -lresult : lresult; } } return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); From 84760bbca9a4ef1bfb38b9c101a2604e5d429605 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:28 -0500 Subject: [PATCH 018/412] cutils: Document differences between parse_uint and qemu_strtou64 These two functions are subtly different, and not just because of swapped parameter order. It took me adding better unit tests to figure out why. Document the differences to make it more obvious to developers trying to pick which one to use, as well as to aid in upcoming semantic changes. While touching the documentation, adjust a mis-statement: parse_uint does not return -EINVAL on invalid base, but assert()s, like all the other qemu_strto* functions that take a base argument. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-7-eblake@redhat.com> --- util/cutils.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/util/cutils.c b/util/cutils.c index 9b6ce9179c..36c14b769f 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -611,6 +611,8 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, * Convert string @nptr to an uint64_t. * * Works like qemu_strtoul(), except it stores UINT64_MAX on overflow. + * (If you want to prohibit negative numbers that wrap around to + * positive, use parse_uint()). */ int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result) @@ -721,7 +723,8 @@ const char *qemu_strchrnul(const char *s, int c) * * @s: String to parse * @value: Destination for parsed integer value - * @endptr: Destination for pointer to first character not consumed + * @endptr: Destination for pointer to first character not consumed, must + * not be %NULL * @base: integer base, between 2 and 36 inclusive, or 0 * * Parse unsigned integer @@ -729,15 +732,16 @@ const char *qemu_strchrnul(const char *s, int c) * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. * - * If @s is null, or @base is invalid, or @s doesn't start with an - * integer in the syntax above, set *@value to 0, *@endptr to @s, and - * return -EINVAL. + * If @s is null, or @s doesn't start with an integer in the syntax + * above, set *@value to 0, *@endptr to @s, and return -EINVAL. * * Set *@endptr to point right beyond the parsed integer (even if the integer * overflows or is negative, all digits will be parsed and *@endptr will * point right beyond them). * * If the integer is negative, set *@value to 0, and return -ERANGE. + * (If you want to allow negative numbers that wrap around within + * bounds, use qemu_strtou64()). * * If the integer overflows unsigned long long, set *@value to * ULLONG_MAX, and return -ERANGE. @@ -794,10 +798,10 @@ out: * * Parse unsigned integer from entire string * - * Have the same behavior of parse_uint(), but with an additional check - * for additional data after the parsed number. If extra characters are present - * after the parsed number, the function will return -EINVAL, and *@v will - * be set to 0. + * Have the same behavior of parse_uint(), but with an additional + * check for additional data after the parsed number. If extra + * characters are present after a non-overflowing parsed number, the + * function will return -EINVAL, and *@v will be set to 0. */ int parse_uint_full(const char *s, unsigned long long *value, int base) { From bd1386cce1b184e4260721858d3bb4b4c888b5f0 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:29 -0500 Subject: [PATCH 019/412] cutils: Adjust signature of parse_uint[_full] It's already confusing that we have two very similar functions for wrapping the parse of a 64-bit unsigned value, differing mainly on whether they permit leading '-'. Adjust the signature of parse_uint() and parse_uint_full() to be like all of qemu_strto*(): put the result parameter last, use the same types (uint64_t and unsigned long long have the same width, but are not always the same type), and mark endptr const (this latter change only affects the rare caller of parse_uint). Adjust all callers in the tree. While at it, note that since cutils.c already includes: QEMU_BUILD_BUG_ON(sizeof(int64_t) != sizeof(long long)); we are guaranteed that the result of parse_uint* cannot exceed UINT64_MAX (or the build would have failed), so we can drop pre-existing dead comparisons in opts-visitor.c that were never false. Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-8-eblake@redhat.com> [eblake: Drop dead code spotted by Markus] Signed-off-by: Eric Blake --- audio/audio_legacy.c | 4 +- block/gluster.c | 4 +- block/nfs.c | 4 +- blockdev.c | 4 +- contrib/ivshmem-server/main.c | 4 +- include/qemu/cutils.h | 5 +- qapi/opts-visitor.c | 12 ++-- tests/unit/test-cutils.c | 119 +++++++++++++++------------------- ui/vnc.c | 4 +- util/cutils.c | 13 ++-- util/guest-random.c | 4 +- util/qemu-sockets.c | 10 +-- 12 files changed, 86 insertions(+), 101 deletions(-) diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c index b848001ff7..dc72ba55e9 100644 --- a/audio/audio_legacy.c +++ b/audio/audio_legacy.c @@ -35,8 +35,8 @@ static uint32_t toui32(const char *str) { - unsigned long long ret; - if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) { + uint64_t ret; + if (parse_uint_full(str, 10, &ret) || ret > UINT32_MAX) { dolog("Invalid integer value `%s'\n", str); exit(1); } diff --git a/block/gluster.c b/block/gluster.c index 185a83e5e5..ad5fadbe79 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -424,7 +424,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, int ret; int old_errno; SocketAddressList *server; - unsigned long long port; + uint64_t port; glfs = glfs_find_preopened(gconf->volume); if (glfs) { @@ -445,7 +445,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, server->value->u.q_unix.path, 0); break; case SOCKET_ADDRESS_TYPE_INET: - if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 || + if (parse_uint_full(server->value->u.inet.port, 10, &port) < 0 || port > 65535) { error_setg(errp, "'%s' is not a valid port number", server->value->u.inet.port); diff --git a/block/nfs.c b/block/nfs.c index 8f89ece69f..c24df49747 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -114,13 +114,13 @@ static int nfs_parse_uri(const char *filename, QDict *options, Error **errp) qdict_put_str(options, "path", uri->path); for (i = 0; i < qp->n; i++) { - unsigned long long val; + uint64_t val; if (!qp->p[i].value) { error_setg(errp, "Value for NFS parameter expected: %s", qp->p[i].name); goto out; } - if (parse_uint_full(qp->p[i].value, &val, 0)) { + if (parse_uint_full(qp->p[i].value, 0, &val)) { error_setg(errp, "Illegal value for NFS parameter: %s", qp->p[i].name); goto out; diff --git a/blockdev.c b/blockdev.c index db2725fe74..e6eba61484 100644 --- a/blockdev.c +++ b/blockdev.c @@ -341,10 +341,10 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, switch (qobject_type(entry->value)) { case QTYPE_QSTRING: { - unsigned long long length; + uint64_t length; const char *str = qstring_get_str(qobject_to(QString, entry->value)); - if (parse_uint_full(str, &length, 10) == 0 && + if (parse_uint_full(str, 10, &length) == 0 && length > 0 && length <= UINT_MAX) { block_acct_add_interval(stats, (unsigned) length); } else { diff --git a/contrib/ivshmem-server/main.c b/contrib/ivshmem-server/main.c index 224dbeb547..5901f17707 100644 --- a/contrib/ivshmem-server/main.c +++ b/contrib/ivshmem-server/main.c @@ -69,7 +69,7 @@ static void ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) { int c; - unsigned long long v; + uint64_t v; Error *err = NULL; while ((c = getopt(argc, argv, "hvFp:S:m:M:l:n:")) != -1) { @@ -112,7 +112,7 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) break; case 'n': /* number of vectors */ - if (parse_uint_full(optarg, &v, 0) < 0) { + if (parse_uint_full(optarg, 0, &v) < 0) { fprintf(stderr, "cannot parse n_vectors\n"); ivshmem_server_help(argv[0]); exit(1); diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 92c436d8c7..92c927a6a3 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -163,9 +163,8 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, int qemu_strtod(const char *nptr, const char **endptr, double *result); int qemu_strtod_finite(const char *nptr, const char **endptr, double *result); -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base); -int parse_uint_full(const char *s, unsigned long long *value, int base); +int parse_uint(const char *s, const char **endptr, int base, uint64_t *value); +int parse_uint_full(const char *s, int base, uint64_t *value); int qemu_strtosz(const char *nptr, const char **end, uint64_t *result); int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result); diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 587f31baf6..8f1efab8b9 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -454,8 +454,8 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) OptsVisitor *ov = to_ov(v); const QemuOpt *opt; const char *str; - unsigned long long val; - char *endptr; + uint64_t val; + const char *endptr; if (ov->list_mode == LM_UNSIGNED_INTERVAL) { *obj = ov->range_next.u; @@ -471,18 +471,18 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) /* we've gotten past lookup_scalar() */ assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); - if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { + if (parse_uint(str, &endptr, 0, &val) == 0) { if (*endptr == '\0') { *obj = val; processed(ov, name); return true; } if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { - unsigned long long val2; + uint64_t val2; str = endptr + 1; - if (parse_uint_full(str, &val2, 0) == 0 && - val2 <= UINT64_MAX && val <= val2 && + if (parse_uint_full(str, 0, &val2) == 0 && + val <= val2 && val2 - val < OPTS_VISITOR_RANGE_MAX) { ov->range_next.u = val; ov->range_limit.u = val2; diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 8d2e057bda..3664b4fffc 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -31,12 +31,11 @@ static void test_parse_uint_null(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; int r; - r = parse_uint(NULL, &i, &endptr, 0); + r = parse_uint(NULL, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -45,13 +44,12 @@ static void test_parse_uint_null(void) static void test_parse_uint_empty(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = ""; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -60,13 +58,12 @@ static void test_parse_uint_empty(void) static void test_parse_uint_whitespace(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " \t "; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -76,13 +73,12 @@ static void test_parse_uint_whitespace(void) static void test_parse_uint_invalid(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " \t xxx"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -92,13 +88,12 @@ static void test_parse_uint_invalid(void) static void test_parse_uint_trailing(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "123xxx"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -107,13 +102,12 @@ static void test_parse_uint_trailing(void) static void test_parse_uint_correct(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "123"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -122,13 +116,12 @@ static void test_parse_uint_correct(void) static void test_parse_uint_octal(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "0123"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 0123); @@ -137,13 +130,12 @@ static void test_parse_uint_octal(void) static void test_parse_uint_decimal(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "0123"; int r; - r = parse_uint(str, &i, &endptr, 10); + r = parse_uint(str, &endptr, 10, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -152,13 +144,12 @@ static void test_parse_uint_decimal(void) static void test_parse_uint_llong_max(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, (unsigned long long)LLONG_MAX + 1); @@ -169,13 +160,12 @@ static void test_parse_uint_llong_max(void) static void test_parse_uint_max(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; char *str = g_strdup_printf("%llu", ULLONG_MAX); int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, ULLONG_MAX); @@ -186,32 +176,31 @@ static void test_parse_uint_max(void) static void test_parse_uint_overflow(void) { - unsigned long long i; - char f = 'X'; - char *endptr; + uint64_t i; + const char *endptr; const char *str; int r; i = 999; - endptr = &f; + endptr = "somewhere"; str = "99999999999999999999999999999999999999"; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); i = 999; - endptr = &f; + endptr = "somewhere"; str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); i = 999; - endptr = &f; + endptr = "somewhere"; str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); @@ -219,24 +208,23 @@ static void test_parse_uint_overflow(void) static void test_parse_uint_negative(void) { - unsigned long long i; - char f = 'X'; - char *endptr; + uint64_t i; + const char *endptr; const char *str; int r; i = 999; - endptr = &f; + endptr = "somewhere"; str = " \t -321"; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str + strlen(str)); i = 999; - endptr = &f; + endptr = "somewhere"; str = "-0xffffffff00000001"; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str + strlen(str)); @@ -244,13 +232,12 @@ static void test_parse_uint_negative(void) static void test_parse_uint_negzero(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " -0"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, 0); @@ -259,11 +246,11 @@ static void test_parse_uint_negzero(void) static void test_parse_uint_full_trailing(void) { - unsigned long long i = 999; + uint64_t i = 999; const char *str = "123xxx"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -271,11 +258,11 @@ static void test_parse_uint_full_trailing(void) static void test_parse_uint_full_correct(void) { - unsigned long long i = 999; + uint64_t i = 999; const char *str = "123"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -284,11 +271,11 @@ static void test_parse_uint_full_correct(void) static void test_parse_uint_full_erange_junk(void) { /* FIXME - inconsistent with qemu_strto* which favors EINVAL */ - unsigned long long i = 999; + uint64_t i = 999; const char *str = "-2junk"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, -ERANGE /* FIXME -EINVAL */); g_assert_cmpuint(i, ==, 0); diff --git a/ui/vnc.c b/ui/vnc.c index 9d8a24dd8a..92964dcc0c 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3728,7 +3728,7 @@ static int vnc_display_get_address(const char *addrstr, } else { const char *port; size_t hostlen; - unsigned long long baseport = 0; + uint64_t baseport = 0; InetSocketAddress *inet; port = strrchr(addrstr, ':'); @@ -3776,7 +3776,7 @@ static int vnc_display_get_address(const char *addrstr, } } else { int offset = reverse ? 0 : 5900; - if (parse_uint_full(port, &baseport, 10) < 0) { + if (parse_uint_full(port, 10, &baseport) < 0) { error_setg(errp, "can't convert to a number: %s", port); goto cleanup; } diff --git a/util/cutils.c b/util/cutils.c index 36c14b769f..0e279a531a 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -722,10 +722,10 @@ const char *qemu_strchrnul(const char *s, int c) * parse_uint: * * @s: String to parse - * @value: Destination for parsed integer value * @endptr: Destination for pointer to first character not consumed, must * not be %NULL * @base: integer base, between 2 and 36 inclusive, or 0 + * @value: Destination for parsed integer value * * Parse unsigned integer * @@ -748,8 +748,7 @@ const char *qemu_strchrnul(const char *s, int c) * * Else, set *@value to the parsed integer, and return 0. */ -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base) +int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) { int r = 0; char *endp = (char *)s; @@ -793,8 +792,8 @@ out: * parse_uint_full: * * @s: String to parse - * @value: Destination for parsed integer value * @base: integer base, between 2 and 36 inclusive, or 0 + * @value: Destination for parsed integer value * * Parse unsigned integer from entire string * @@ -803,12 +802,12 @@ out: * characters are present after a non-overflowing parsed number, the * function will return -EINVAL, and *@v will be set to 0. */ -int parse_uint_full(const char *s, unsigned long long *value, int base) +int parse_uint_full(const char *s, int base, uint64_t *value) { - char *endp; + const char *endp; int r; - r = parse_uint(s, value, &endp, base); + r = parse_uint(s, &endp, base, value); if (r < 0) { return r; } diff --git a/util/guest-random.c b/util/guest-random.c index a24d27624c..9465dda085 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -89,8 +89,8 @@ void qemu_guest_random_seed_thread_part2(uint64_t seed) int qemu_guest_random_seed_main(const char *optarg, Error **errp) { - unsigned long long seed; - if (parse_uint_full(optarg, &seed, 0)) { + uint64_t seed; + if (parse_uint_full(optarg, 0, &seed)) { error_setg(errp, "Invalid seed number: %s", optarg); return -1; } else { diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index c06a4dce77..892d33f5e6 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -249,12 +249,12 @@ static int inet_listen_saddr(InetSocketAddress *saddr, /* lookup */ if (port_offset) { - unsigned long long baseport; + uint64_t baseport; if (strlen(port) == 0) { error_setg(errp, "port not specified"); return -1; } - if (parse_uint_full(port, &baseport, 10) < 0) { + if (parse_uint_full(port, 10, &baseport) < 0) { error_setg(errp, "can't convert to a number: %s", port); return -1; } @@ -732,19 +732,19 @@ static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, struct sockaddr_vm *svm, Error **errp) { - unsigned long long val; + uint64_t val; memset(svm, 0, sizeof(*svm)); svm->svm_family = AF_VSOCK; - if (parse_uint_full(vaddr->cid, &val, 10) < 0 || + if (parse_uint_full(vaddr->cid, 10, &val) < 0 || val > UINT32_MAX) { error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); return false; } svm->svm_cid = val; - if (parse_uint_full(vaddr->port, &val, 10) < 0 || + if (parse_uint_full(vaddr->port, 10, &val) < 0 || val > UINT32_MAX) { error_setg(errp, "Failed to parse port '%s'", vaddr->port); return false; From 52d606aa5b6859bf13b026aa82f53f8506aa6abe Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:30 -0500 Subject: [PATCH 020/412] cutils: Allow NULL endptr in parse_uint() All the qemu_strto*() functions permit a NULL endptr, just like their libc counterparts, leaving parse_uint() as the oddball that caused SEGFAULT on NULL and required the user to call parse_uint_full() instead. Relax things for consistency, even though the testsuite is the only impacted caller. Add one more unit test to ensure even parse_uint_full(NULL, 0, &value) works. This also fixes our code to uniformly favor EINVAL over ERANGE when both apply. Also fixes a doc mismatch @v vs. a parameter named value. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-9-eblake@redhat.com> --- tests/unit/test-cutils.c | 18 ++++++++++++++++-- util/cutils.c | 34 ++++++++++++---------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 3664b4fffc..cd8fe7d940 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -270,14 +270,26 @@ static void test_parse_uint_full_correct(void) static void test_parse_uint_full_erange_junk(void) { - /* FIXME - inconsistent with qemu_strto* which favors EINVAL */ + /* EINVAL has priority over ERANGE */ uint64_t i = 999; const char *str = "-2junk"; int r; r = parse_uint_full(str, 0, &i); - g_assert_cmpint(r, ==, -ERANGE /* FIXME -EINVAL */); + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpuint(i, ==, 0); +} + +static void test_parse_uint_full_null(void) +{ + uint64_t i = 999; + const char *str = NULL; + int r; + + r = parse_uint_full(str, 0, &i); + + g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); } @@ -3328,6 +3340,8 @@ int main(int argc, char **argv) test_parse_uint_full_correct); g_test_add_func("/cutils/parse_uint_full/erange_junk", test_parse_uint_full_erange_junk); + g_test_add_func("/cutils/parse_uint_full/null", + test_parse_uint_full_null); /* qemu_strtoi() tests */ g_test_add_func("/cutils/qemu_strtoi/correct", diff --git a/util/cutils.c b/util/cutils.c index 0e279a531a..56a2aced8d 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -722,8 +722,7 @@ const char *qemu_strchrnul(const char *s, int c) * parse_uint: * * @s: String to parse - * @endptr: Destination for pointer to first character not consumed, must - * not be %NULL + * @endptr: Destination for pointer to first character not consumed * @base: integer base, between 2 and 36 inclusive, or 0 * @value: Destination for parsed integer value * @@ -737,7 +736,8 @@ const char *qemu_strchrnul(const char *s, int c) * * Set *@endptr to point right beyond the parsed integer (even if the integer * overflows or is negative, all digits will be parsed and *@endptr will - * point right beyond them). + * point right beyond them). If @endptr is %NULL, any trailing character + * instead causes a result of -EINVAL with *@value of 0. * * If the integer is negative, set *@value to 0, and return -ERANGE. * (If you want to allow negative numbers that wrap around within @@ -784,7 +784,12 @@ int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) out: *value = val; - *endptr = endp; + if (endptr) { + *endptr = endp; + } else if (s && *endp) { + r = -EINVAL; + *value = 0; + } return r; } @@ -795,28 +800,13 @@ out: * @base: integer base, between 2 and 36 inclusive, or 0 * @value: Destination for parsed integer value * - * Parse unsigned integer from entire string + * Parse unsigned integer from entire string, rejecting any trailing slop. * - * Have the same behavior of parse_uint(), but with an additional - * check for additional data after the parsed number. If extra - * characters are present after a non-overflowing parsed number, the - * function will return -EINVAL, and *@v will be set to 0. + * Shorthand for parse_uint(s, NULL, base, value). */ int parse_uint_full(const char *s, int base, uint64_t *value) { - const char *endp; - int r; - - r = parse_uint(s, &endp, base, value); - if (r < 0) { - return r; - } - if (*endp) { - *value = 0; - return -EINVAL; - } - - return 0; + return parse_uint(s, NULL, base, value); } int qemu_parse_fd(const char *param) From 759573d05b808344f7047f893d2dd095884dfa4d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:31 -0500 Subject: [PATCH 021/412] test-cutils: Add coverage of qemu_strtod It's hard to tweak code for consistency if I can't prove what will or won't break from those tweaks. Time to add unit tests for qemu_strtod() and qemu_strtod_finite(). Among other things, I wrote a check whether we have C99 semantics for strtod("0x1") (which MUST parse hex numbers) rather than C89 (which must stop parsing at 'x'). These days, I suspect that is okay; but if it fails CI checks, knowing the difference will help us decide what we want to do about it. Note that C2x, while not final at the time of this patch, has been considering whether to make strtol("0b1") parse as 1 with no slop instead of the C17 parse of 0 with slop "b1"; that decision may also bleed over to strtod(). But for now, I didn't think it worth adding unit tests on that front (to strtol or strtod) as things may still change. Likewise, there are plenty more corner cases of strtod proper that I don't explicitly test here, but there are enough unit tests added here that it covers all the branches reached in our wrappers. In particular, it demonstrates the difference on when *value is left uninitialized, which an upcoming patch will normalize. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-10-eblake@redhat.com> --- tests/unit/test-cutils.c | 512 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index cd8fe7d940..0dc9429154 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -25,6 +25,8 @@ * THE SOFTWARE. */ +#include + #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/units.h" @@ -2789,6 +2791,487 @@ static void test_qemu_strtou64_full_erange_junk(void) g_assert_cmpuint(res, ==, UINT64_MAX); } +static void test_qemu_strtod_simple(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* no radix or exponent */ + str = "1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 1); + + /* leading space and sign */ + str = " -0.0"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 5); + + /* fraction only */ + str = "+.5"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 3); + + /* exponent */ + str = "1.e+1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 10.0); + g_assert_true(endptr == str + 5); + + /* hex without radix */ + str = "0x10"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 16.0); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_einval(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* empty */ + str = ""; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + /* NULL */ + str = NULL; + endptr = "random"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_null(endptr); + + /* not recognizable */ + str = " junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_erange(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* overflow */ + str = "9e999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, HUGE_VAL); + g_assert_true(endptr == str + 5); + + str = "-9e+999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, -HUGE_VAL); + g_assert_true(endptr == str + 7); + + /* underflow */ + str = "-9e-999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, >=, -DBL_MIN); + g_assert_cmpfloat(res, <=, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 7); +} + +static void test_qemu_strtod_nonfinite(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* infinity */ + str = "inf"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isinf(res)); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 3); + + str = "-infinity"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isinf(res)); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 9); + + /* not a number */ + str = " NaN"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isnan(res)); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_trailing(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* trailing whitespace */ + str = "1. "; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 1.0); + + /* trailing e is not an exponent */ + str = ".5e"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.5); + + /* trailing ( not part of long NaN */ + str = "nan("; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isnan(res)); + g_assert_true(endptr == str + 3); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_true(isnan(res)); +} + +static void test_qemu_strtod_erange_junk(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* ERANGE with trailing junk... */ + str = "1e-999junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, <=, DBL_MIN); + g_assert_cmpfloat(res, >=, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 6); + + /* ...has less priority than EINVAL when full parse not possible */ + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); +} + +static void test_qemu_strtod_finite_simple(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* no radix or exponent */ + str = "1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 1); + + /* leading space and sign */ + str = " -0.0"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 5); + + /* fraction only */ + str = "+.5"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 3); + + /* exponent */ + str = "1.e+1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 10.0); + g_assert_true(endptr == str + 5); + + /* hex without radix */ + str = "0x10"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 16.0); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_finite_einval(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* empty */ + str = ""; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + /* NULL */ + str = NULL; + endptr = "random"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_null(endptr); + + /* not recognizable */ + str = " junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_finite_erange(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* overflow */ + str = "9e999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, HUGE_VAL); + g_assert_true(endptr == str + 5); + + str = "-9e+999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, -HUGE_VAL); + g_assert_true(endptr == str + 7); + + /* underflow */ + str = "-9e-999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, >=, -DBL_MIN); + g_assert_cmpfloat(res, <=, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 7); +} + +static void test_qemu_strtod_finite_nonfinite(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* infinity */ + str = "inf"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + str = "-infinity"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + /* not a number */ + str = " NaN"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_finite_trailing(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* trailing whitespace */ + str = "1. "; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + + /* trailing e is not an exponent */ + str = ".5e"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + + /* trailing ( not part of long NaN */ + str = "nan("; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); +} + +static void test_qemu_strtod_finite_erange_junk(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* ERANGE with trailing junk... */ + str = "1e-999junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, <=, DBL_MIN); + g_assert_cmpfloat(res, >=, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 6); + + /* ...has less priority than EINVAL when full parse not possible */ + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); +} + static void test_qemu_strtosz_simple(void) { const char *str; @@ -3631,6 +4114,35 @@ int main(int argc, char **argv) g_test_add_func("/cutils/qemu_strtou64_full/erange_junk", test_qemu_strtou64_full_erange_junk); + /* qemu_strtod() tests */ + g_test_add_func("/cutils/qemu_strtod/simple", + test_qemu_strtod_simple); + g_test_add_func("/cutils/qemu_strtod/einval", + test_qemu_strtod_einval); + g_test_add_func("/cutils/qemu_strtod/erange", + test_qemu_strtod_erange); + g_test_add_func("/cutils/qemu_strtod/nonfinite", + test_qemu_strtod_nonfinite); + g_test_add_func("/cutils/qemu_strtod/trailing", + test_qemu_strtod_trailing); + g_test_add_func("/cutils/qemu_strtod/erange_junk", + test_qemu_strtod_erange_junk); + + /* qemu_strtod_finite() tests */ + g_test_add_func("/cutils/qemu_strtod_finite/simple", + test_qemu_strtod_finite_simple); + g_test_add_func("/cutils/qemu_strtod_finite/einval", + test_qemu_strtod_finite_einval); + g_test_add_func("/cutils/qemu_strtod_finite/erange", + test_qemu_strtod_finite_erange); + g_test_add_func("/cutils/qemu_strtod_finite/nonfinite", + test_qemu_strtod_finite_nonfinite); + g_test_add_func("/cutils/qemu_strtod_finite/trailing", + test_qemu_strtod_finite_trailing); + g_test_add_func("/cutils/qemu_strtod_finite/erange_junk", + test_qemu_strtod_finite_erange_junk); + + /* qemu_strtosz() tests */ g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); g_test_add_func("/cutils/strtosz/hex", From edafce694a53bb5ca3025fce0f3dfe7a40056783 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:32 -0500 Subject: [PATCH 022/412] test-cutils: Prepare for upcoming semantic change in qemu_strtosz A quick search for 'qemu_strtosz' in the code base shows that outside of the testsuite, the ONLY place that passes a non-NULL pointer to @endptr of any variant of a size parser is in hmp.c (the 'o' parser of monitor_parse_arguments), and that particular caller warns of "extraneous characters at the end of line" unless the trailing bytes are purely whitespace. Thus, it makes no semantic difference at the high level whether we parse "1.5e1k" as "1" + ".5e1" + "k" (an attempt to use scientific notation in strtod with a scaling suffix of 'k' with no trailing junk, but which qemu_strtosz says should fail with EINVAL), or as "1.5e" + "1k" (a valid size with scaling suffix of 'e' for exabytes, followed by two junk bytes) - either way, any user passing such a string will get an error message about a parse failure. However, an upcoming patch to qemu_strtosz will fix other corner case bugs in handling the fractional portion of a size, and in doing so, it is easier to declare that qemu_strtosz() itself stops parsing at the first 'e' rather than blindly consuming whatever strtod() will recognize. Once that is fixed, the difference will be visible at the low level (getting a valid parse with trailing garbage when @endptr is non-NULL, while continuing to get -EINVAL when @endptr is NULL); this is easier to demonstrate by moving the affected strings from test_qemu_strtosz_invalid() (which declares them as always -EINVAL) to test_qemu_strtosz_trailing() (where @endptr affects behavior, for now with FIXME comments). Note that a similar argument could be made for having "0x1.5" or "0x1M" parse as 0x1 with ".5" or "M" as trailing junk, instead of blindly treating it as -EINVAL; however, as these cases do not suffer from the same problems as floating point, they are not worth changing at this time. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-11-eblake@redhat.com> --- tests/unit/test-cutils.c | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 0dc9429154..8ff291300f 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3563,21 +3563,6 @@ static void test_qemu_strtosz_invalid(void) g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); - /* No floating point exponents */ - str = "1.5e1k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "1.5E+0k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - /* No hex fractions */ str = "0x1.8k"; endptr = NULL; @@ -3681,6 +3666,33 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmphex(res, ==, 0xbaadf00d); + + /* FIXME should stop parse after 'e'. No floating point exponents */ + str = "1.5e1k"; + endptr = NULL; + res = 0xbaadf00d; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); + g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); + g_assert_true(endptr == str /* FIXME + 4 */); + + res = 0xbaadf00d; + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0xbaadf00d); + + str = "1.5E+0k"; + endptr = NULL; + res = 0xbaadf00d; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); + g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); + g_assert_true(endptr == str /* FIXME + 4 */); + + res = 0xbaadf00d; + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmphex(res, ==, 0xbaadf00d); } static void test_qemu_strtosz_erange(void) From 157367cf21ea01a9413fd6f16808b0bf34804138 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:33 -0500 Subject: [PATCH 023/412] test-cutils: Refactor qemu_strtosz tests for less boilerplate No need to copy-and-paste lots of boilerplate per string tested, when we can consolidate that behind helper functions. Plus, this adds a bit more coverage (we now test all strings both with and without endptr, whereas before some tests skipped the NULL endptr case), which exposed a SEGFAULT on qemu_strtosz(NULL, NULL, &val) that will be fixed in an upcoming patch. Note that duplicating boilerplate has one advantage lost here - a failed test tells you which line number failed; but a helper function does not show the call stack that reached the failure. Since we call the helper more than once within many of the "unit tests", even the unit test name doesn't point out which call is failing. But that only matters when tests fail (they normally pass); at which point I'm debugging the failures under gdb anyways, so I'm not too worried about it. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-12-eblake@redhat.com> --- tests/unit/test-cutils.c | 503 ++++++++------------------------------- 1 file changed, 100 insertions(+), 403 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 8ff291300f..fa94d15891 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3272,473 +3272,170 @@ static void test_qemu_strtod_finite_erange_junk(void) g_assert_cmpfloat(res, ==, 999.0); } +typedef int (*qemu_strtosz_fn)(const char *, const char **, uint64_t *); +static void do_strtosz_full(const char *str, qemu_strtosz_fn fn, + int exp_ptr_ret, uint64_t exp_ptr_val, + size_t exp_ptr_offset, int exp_null_ret, + uint64_t exp_null_val) +{ + const char *endptr = "somewhere"; + uint64_t val = 0xbaadf00d; + int ret; + + ret = fn(str, &endptr, &val); + g_assert_cmpint(ret, ==, exp_ptr_ret); + g_assert_cmpuint(val, ==, exp_ptr_val); + g_assert_true(endptr == str + exp_ptr_offset); + + val = 0xbaadf00d; + ret = fn(str, NULL, &val); + g_assert_cmpint(ret, ==, exp_null_ret); + g_assert_cmpuint(val, ==, exp_null_val); +} + +static void do_strtosz(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void do_strtosz_MiB(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz_MiB, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void do_strtosz_metric(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz_metric, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + static void test_qemu_strtosz_simple(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; - - str = "0"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 1); + do_strtosz("0", 0, 0, 1); /* Leading 0 gives decimal results, not octal */ - str = "08"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 8); - g_assert_true(endptr == str + 2); + do_strtosz("08", 0, 8, 2); /* Leading space is ignored */ - str = " 12345"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); - g_assert_true(endptr == str + 6); + do_strtosz(" 12345", 0, 12345, 6); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); + /* 2^53-1 */ + do_strtosz("9007199254740991", 0, 0x1fffffffffffffULL, 16); - str = "9007199254740991"; /* 2^53-1 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x1fffffffffffffULL); - g_assert_true(endptr == str + 16); + /* 2^53 */ + do_strtosz("9007199254740992", 0, 0x20000000000000ULL, 16); - str = "9007199254740992"; /* 2^53 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x20000000000000ULL); - g_assert_true(endptr == str + 16); + /* 2^53+1 */ + do_strtosz("9007199254740993", 0, 0x20000000000001ULL, 16); - str = "9007199254740993"; /* 2^53+1 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x20000000000001ULL); - g_assert_true(endptr == str + 16); + /* 0xfffffffffffff800 (53 msbs set) */ + do_strtosz("18446744073709549568", 0, 0xfffffffffffff800ULL, 20); - str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0xfffffffffffff800ULL); - g_assert_true(endptr == str + 20); + /* 0xfffffffffffffbff */ + do_strtosz("18446744073709550591", 0, 0xfffffffffffffbffULL, 20); - str = "18446744073709550591"; /* 0xfffffffffffffbff */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0xfffffffffffffbffULL); - g_assert_true(endptr == str + 20); - - str = "18446744073709551615"; /* 0xffffffffffffffff */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0xffffffffffffffffULL); - g_assert_true(endptr == str + 20); + /* 0xffffffffffffffff */ + do_strtosz("18446744073709551615", 0, 0xffffffffffffffffULL, 20); } static void test_qemu_strtosz_hex(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; + do_strtosz("0x0", 0, 0, 3); - str = "0x0"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 3); + do_strtosz("0xab", 0, 171, 4); - str = "0xab"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 171); - g_assert_true(endptr == str + 4); - - str = "0xae"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 174); - g_assert_true(endptr == str + 4); + do_strtosz("0xae", 0, 174, 4); } static void test_qemu_strtosz_units(void) { - const char *none = "1"; - const char *b = "1B"; - const char *k = "1K"; - const char *m = "1M"; - const char *g = "1G"; - const char *t = "1T"; - const char *p = "1P"; - const char *e = "1E"; - int err; - const char *endptr; - uint64_t res; - /* default is M */ - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz_MiB(none, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, MiB); - g_assert_true(endptr == none + 1); + do_strtosz_MiB("1", 0, MiB, 1); - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(b, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1); - g_assert_true(endptr == b + 2); + do_strtosz("1B", 0, 1, 2); - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(k, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, KiB); - g_assert_true(endptr == k + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(m, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, MiB); - g_assert_true(endptr == m + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(g, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, GiB); - g_assert_true(endptr == g + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(t, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, TiB); - g_assert_true(endptr == t + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(p, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, PiB); - g_assert_true(endptr == p + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(e, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, EiB); - g_assert_true(endptr == e + 2); + do_strtosz("1K", 0, KiB, 2); + do_strtosz("1M", 0, MiB, 2); + do_strtosz("1G", 0, GiB, 2); + do_strtosz("1T", 0, TiB, 2); + do_strtosz("1P", 0, PiB, 2); + do_strtosz("1E", 0, EiB, 2); } static void test_qemu_strtosz_float(void) { - const char *str; - int err; - const char *endptr; - uint64_t res; - - str = "0.5E"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, EiB / 2); - g_assert_true(endptr == str + 4); + do_strtosz("0.5E", 0, EiB / 2, 4); /* For convenience, a fraction of 0 is tolerated even on bytes */ - str = "1.0B"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1); - g_assert_true(endptr == str + 4); + do_strtosz("1.0B", 0, 1, 4); /* An empty fraction is tolerated */ - str = "1.k"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1024); - g_assert_true(endptr == str + 3); + do_strtosz("1.k", 0, 1024, 3); /* For convenience, we permit values that are not byte-exact */ - str = "12.345M"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); - g_assert_true(endptr == str + 7); + do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); } static void test_qemu_strtosz_invalid(void) { - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; - - str = ""; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = " \t "; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "crap"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "inf"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "NaN"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); + do_strtosz("crap", -EINVAL, 0xbaadf00d, 0); + do_strtosz("inf", -EINVAL, 0xbaadf00d, 0); + do_strtosz("NaN", -EINVAL, 0xbaadf00d, 0); /* Fractional values require scale larger than bytes */ - str = "1.1B"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "1.1"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("1.1B", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1", -EINVAL, 0xbaadf00d, 0); /* No hex fractions */ - str = "0x1.8k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("0x1.8k", -EINVAL, 0xbaadf00d, 0); /* No suffixes */ - str = "0x18M"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("0x18M", -EINVAL, 0xbaadf00d, 0); /* No negative values */ - str = "-0"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "-1"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("-0", -EINVAL, 0xbaadf00d, 0); + do_strtosz("-1", -EINVAL, 0xbaadf00d, 0); } static void test_qemu_strtosz_trailing(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; + do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, + -EINVAL, 0xbaadf00d); - str = "123xxx"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz_MiB(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123 * MiB); - g_assert_true(endptr == str + 3); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "1kiB"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1024); - g_assert_true(endptr == str + 2); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "0x"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 1); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "0.NaN"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 2); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "123-45"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert_true(endptr == str + 3); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); + do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0xbaadf00d); + do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); /* FIXME should stop parse after 'e'. No floating point exponents */ - str = "1.5e1k"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); - g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); - g_assert_true(endptr == str /* FIXME + 4 */); + do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, + 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0xbaadf00d); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - - str = "1.5E+0k"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); - g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); - g_assert_true(endptr == str /* FIXME + 4 */); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); + do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, + 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0xbaadf00d); } static void test_qemu_strtosz_erange(void) { - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; + /* 2^64; see strtosz_simple for 2^64-1 */ + do_strtosz("18446744073709551616", -ERANGE, 0xbaadf00d, 20); - str = "18446744073709551616"; /* 2^64; see strtosz_simple for 2^64-1 */ - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str + 20); - - str = "20E"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str + 3); + do_strtosz("20E", -ERANGE, 0xbaadf00d, 3); } static void test_qemu_strtosz_metric(void) { - const char *str; - int err; - const char *endptr; - uint64_t res; - - str = "12345k"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345000); - g_assert_true(endptr == str + 6); - - str = "12.345M"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345000); - g_assert_true(endptr == str + 7); + do_strtosz_metric("12345k", 0, 12345000, 6); + do_strtosz_metric("12.345M", 0, 12345000, 7); } static void test_freq_to_str(void) From f49371ecae182eb07cfb105aa7ea637479d83764 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:34 -0500 Subject: [PATCH 024/412] cutils: Allow NULL str in qemu_strtosz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the other qemu_strto* and parse_uint allow a NULL str. Having qemu_strtosz not crash on qemu_strtosz(NULL, NULL, &value) is an easy fix that adds some consistency between our string parsers. Signed-off-by: Eric Blake Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-13-eblake@redhat.com> --- tests/unit/test-cutils.c | 10 +++++++++- util/cutils.c | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index fa94d15891..095a611d1e 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3285,7 +3285,12 @@ static void do_strtosz_full(const char *str, qemu_strtosz_fn fn, ret = fn(str, &endptr, &val); g_assert_cmpint(ret, ==, exp_ptr_ret); g_assert_cmpuint(val, ==, exp_ptr_val); - g_assert_true(endptr == str + exp_ptr_offset); + if (str) { + g_assert_true(endptr == str + exp_ptr_offset); + } else { + g_assert_cmpint(exp_ptr_offset, ==, 0); + g_assert_null(endptr); + } val = 0xbaadf00d; ret = fn(str, NULL, &val); @@ -3383,6 +3388,9 @@ static void test_qemu_strtosz_float(void) static void test_qemu_strtosz_invalid(void) { + do_strtosz(NULL, -EINVAL, 0xbaadf00d, 0); + + /* Must parse at least one digit */ do_strtosz("", -EINVAL, 0xbaadf00d, 0); do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); do_strtosz("crap", -EINVAL, 0xbaadf00d, 0); diff --git a/util/cutils.c b/util/cutils.c index 56a2aced8d..1dc67d201d 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -306,7 +306,7 @@ static int do_strtosz(const char *nptr, const char **end, out: if (end) { *end = endptr; - } else if (*endptr) { + } else if (nptr && *endptr) { retval = -EINVAL; } if (retval == 0) { From a73049b2a1bad81c1059b79cf4567e4d6932634f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:35 -0500 Subject: [PATCH 025/412] numa: Check for qemu_strtosz_MiB error As shown in the previous commit, qemu_strtosz_MiB sometimes leaves the result value untouched (we have to audit further to learn that in that case, the QAPI generator says that visit_type_NumaOptions() will have zero-initialized it), and sometimes leaves it with the value of a partial parse before -EINVAL occurs because of trailing garbage. Rather than blindly treating any string the user may throw at us as valid, we should check for parse failures. Fixes: cc001888 ("numa: fixup parsed NumaNodeOptions earlier", v2.11.0) Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-14-eblake@redhat.com> --- hw/core/numa.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/core/numa.c b/hw/core/numa.c index d8d36b16d8..f08956ddb0 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -531,10 +531,17 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) /* Fix up legacy suffix-less format */ if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { const char *mem_str = qemu_opt_get(opts, "mem"); - qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + int ret = qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + + if (ret < 0) { + error_setg_errno(&err, -ret, "could not parse memory size '%s'", + mem_str); + } } - set_numa_options(ms, object, &err); + if (!err) { + set_numa_options(ms, object, &err); + } qapi_free_NumaOptions(object); if (err) { From e1cf34b6b33caea5e47d46ba715eca6603fdbbc4 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:36 -0500 Subject: [PATCH 026/412] test-cutils: Add more coverage to qemu_strtosz Add some more strings that the user might send our way. In particular, some of these additions include FIXME comments showing where our parser doesn't quite behave the way we want. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-15-eblake@redhat.com> --- tests/unit/test-cutils.c | 140 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 129 insertions(+), 11 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 095a611d1e..8ea6065855 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3326,8 +3326,8 @@ static void test_qemu_strtosz_simple(void) /* Leading 0 gives decimal results, not octal */ do_strtosz("08", 0, 8, 2); - /* Leading space is ignored */ - do_strtosz(" 12345", 0, 12345, 6); + /* Leading space and + are ignored */ + do_strtosz(" +12345", 0, 12345, 7); /* 2^53-1 */ do_strtosz("9007199254740991", 0, 0x1fffffffffffffULL, 16); @@ -3354,17 +3354,27 @@ static void test_qemu_strtosz_hex(void) do_strtosz("0xab", 0, 171, 4); - do_strtosz("0xae", 0, 174, 4); + do_strtosz(" +0xae", 0, 174, 6); } static void test_qemu_strtosz_units(void) { - /* default is M */ + /* default scale depends on function */ + do_strtosz("1", 0, 1, 1); do_strtosz_MiB("1", 0, MiB, 1); + do_strtosz_metric("1", 0, 1, 1); + /* Explicit byte suffix works for all functions */ do_strtosz("1B", 0, 1, 2); + do_strtosz_MiB("1B", 0, 1, 2); + do_strtosz_metric("1B", 0, 1, 2); + /* Expose the scale */ do_strtosz("1K", 0, KiB, 2); + do_strtosz_MiB("1K", 0, KiB, 2); + do_strtosz_metric("1K", 0, 1000, 2); + + /* Other suffixes, see also test_qemu_strtosz_metric */ do_strtosz("1M", 0, MiB, 2); do_strtosz("1G", 0, GiB, 2); do_strtosz("1T", 0, TiB, 2); @@ -3376,14 +3386,37 @@ static void test_qemu_strtosz_float(void) { do_strtosz("0.5E", 0, EiB / 2, 4); + /* Implied M suffix okay */ + do_strtosz_MiB("0.5", 0, MiB / 2, 3); + /* For convenience, a fraction of 0 is tolerated even on bytes */ do_strtosz("1.0B", 0, 1, 4); - /* An empty fraction is tolerated */ + /* An empty fraction tail is tolerated */ do_strtosz("1.k", 0, 1024, 3); + /* FIXME An empty fraction head should be tolerated */ + do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME 512 */, + 0 /* FIXME 4 */); + /* For convenience, we permit values that are not byte-exact */ do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); + + /* FIXME Fraction tail should round correctly */ + do_strtosz("1.9999k", 0, 2048, 7); + do_strtosz("1.9999999999999999999999999999999999999999999999999999k", 0, + 1024 /* FIXME 2048 */, 55); + + /* FIXME ERANGE underflow in the fraction tail should not matter for 'k' */ + do_strtosz("1." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1k", 0, 1 /* FIXME 1024 */, 354); } static void test_qemu_strtosz_invalid(void) @@ -3393,57 +3426,142 @@ static void test_qemu_strtosz_invalid(void) /* Must parse at least one digit */ do_strtosz("", -EINVAL, 0xbaadf00d, 0); do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); - do_strtosz("crap", -EINVAL, 0xbaadf00d, 0); + do_strtosz(".", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" .", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" .k", -EINVAL, 0xbaadf00d, 0); do_strtosz("inf", -EINVAL, 0xbaadf00d, 0); do_strtosz("NaN", -EINVAL, 0xbaadf00d, 0); + /* Lone suffix is not okay */ + do_strtosz("k", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" M", -EINVAL, 0xbaadf00d, 0); + /* Fractional values require scale larger than bytes */ do_strtosz("1.1B", -EINVAL, 0xbaadf00d, 0); do_strtosz("1.1", -EINVAL, 0xbaadf00d, 0); + /* FIXME underflow in the fraction tail should matter for 'B' */ + do_strtosz("1.00001B", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.00000000000000000001B", 0 /* FIXME -EINVAL */, + 1 /* FIXME 0xbaadf00d */, 23 /* FIXME 0 */); + do_strtosz("1." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */, + 354 /* FIXME 0 */); + /* No hex fractions */ do_strtosz("0x1.8k", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x1.k", -EINVAL, 0xbaadf00d, 0); - /* No suffixes */ + /* No hex suffixes */ do_strtosz("0x18M", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x1p1", -EINVAL, 0xbaadf00d, 0); - /* No negative values */ - do_strtosz("-0", -EINVAL, 0xbaadf00d, 0); - do_strtosz("-1", -EINVAL, 0xbaadf00d, 0); + /* decimal in place of scaling suffix */ + do_strtosz("1.1.k", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1.", -EINVAL, 0xbaadf00d, 0); } static void test_qemu_strtosz_trailing(void) { + /* Trailing whitespace */ + do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + + /* Unknown suffix overrides even implied scale*/ + do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); + + /* Implied scale allows partial parse */ do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, -EINVAL, 0xbaadf00d); + do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, + -EINVAL, 0xbaadf00d); + /* Junk after one-byte suffix */ do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + + /* Incomplete hex is an unknown suffix */ do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0xbaadf00d); + + /* Hex literals use only one leading zero */ + do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + + /* No support for binary literals; 'b' is valid suffix */ + do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + + /* Junk after decimal */ do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + + /* Although negatives are invalid, '-' may be in trailing junk */ do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); + do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0xbaadf00d); /* FIXME should stop parse after 'e'. No floating point exponents */ do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, -EINVAL, 0xbaadf00d); - do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, -EINVAL, 0xbaadf00d); + + /* + * FIXME overflow in fraction is so buggy it can read beyond bounds + * if we don't stuff extra \0 in our literal + */ + do_strtosz_full("1.5E999\0\0" /* FIXME 1.5E999" */, qemu_strtosz, + 0, 1 /* FIXME EiB * 1.5 */, 8 /* FIXME 4 */, + 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */); } static void test_qemu_strtosz_erange(void) { + /* FIXME negative values fit better as ERANGE */ + do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 3 */); + do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 2 */); + do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, + 0xbaadf00d, 0 /* FIXME 2 */, -EINVAL, 0xbaadf00d); + do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, + 0 /* FIXME 4 */); + do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, + 0xbaadf00d, 0 /* FIXME 3 */, -EINVAL, 0xbaadf00d); + do_strtosz_full(" -." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, + 0xbaadf00d, 0 /* FIXME 354 */, -EINVAL, 0xbaadf00d); + /* 2^64; see strtosz_simple for 2^64-1 */ do_strtosz("18446744073709551616", -ERANGE, 0xbaadf00d, 20); do_strtosz("20E", -ERANGE, 0xbaadf00d, 3); + + /* FIXME Fraction tail can cause ERANGE overflow */ + do_strtosz("15.9999999999999999999999999999999999999999999999999999E", + 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0xbaadf00d */, 56); + + /* EINVAL has priority over ERANGE */ + do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0xbaadf00d, 7, + -EINVAL, 0xbaadf00d); } static void test_qemu_strtosz_metric(void) { do_strtosz_metric("12345k", 0, 12345000, 6); do_strtosz_metric("12.345M", 0, 12345000, 7); + + /* Fraction is affected by floating-point rounding */ + /* This would be 0xfffffffffffffbff with infinite precision */ + do_strtosz_metric("18.446744073709550591E", 0, 0xfffffffffffffc0cULL, 22); } static void test_freq_to_str(void) From 896fbd90aa3ffd4ad57b35722128fe65a9abc530 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:37 -0500 Subject: [PATCH 027/412] cutils: Set value in all qemu_strtosz* error paths Making callers determine whether or not *value was populated on error is not nice for usability. Pre-patch, we have unit tests that check that *result is left unchanged on most EINVAL errors and set to 0 on many ERANGE errors. This is subtly different from libc strtoumax() behavior which returns UINT64_MAX on ERANGE errors, as well as different from our parse_uint() which slams to 0 on EINVAL on the grounds that we want our functions to be harder to mis-use than strtoumax(). Let's audit callers: - hw/core/numa.c:parse_numa() fixed in the previous patch to check for errors - migration/migration-hmp-cmds.c:hmp_migrate_set_parameter(), monitor/hmp.c:monitor_parse_arguments(), qapi/opts-visitor.c:opts_type_size(), qapi/qobject-input-visitor.c:qobject_input_type_size_keyval(), qemu-img.c:cvtnum_full(), qemu-io-cmds.c:cvtnum(), target/i386/cpu.c:x86_cpu_parse_featurestr(), and util/qemu-option.c:parse_option_size() appear to reject all failures (although some with distinct messages for ERANGE as opposed to EINVAL), so it doesn't matter what is in the value parameter on error. - All remaining callers are in the testsuite, where we can tweak our expectations to match our new desired behavior. Advancing to the end of the string parsed on overflow (ERANGE), while still returning 0, makes sense (UINT64_MAX as a size is unlikely to be useful); likewise, our size parsing code is complex enough that it's easier to always return 0 when endptr is NULL but trailing garbage was found, rather than trying to return the value of the prefix actually parsed (no current caller cared about the value of the prefix). Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-16-eblake@redhat.com> --- tests/unit/test-cutils.c | 106 +++++++++++++++++++-------------------- util/cutils.c | 17 +++++-- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 8ea6065855..f4dfb6ae14 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3396,7 +3396,7 @@ static void test_qemu_strtosz_float(void) do_strtosz("1.k", 0, 1024, 3); /* FIXME An empty fraction head should be tolerated */ - do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME 512 */, + do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0 /* FIXME 512 */, 0 /* FIXME 4 */); /* For convenience, we permit values that are not byte-exact */ @@ -3421,29 +3421,29 @@ static void test_qemu_strtosz_float(void) static void test_qemu_strtosz_invalid(void) { - do_strtosz(NULL, -EINVAL, 0xbaadf00d, 0); + do_strtosz(NULL, -EINVAL, 0, 0); /* Must parse at least one digit */ - do_strtosz("", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); - do_strtosz(".", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" .", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" .k", -EINVAL, 0xbaadf00d, 0); - do_strtosz("inf", -EINVAL, 0xbaadf00d, 0); - do_strtosz("NaN", -EINVAL, 0xbaadf00d, 0); + do_strtosz("", -EINVAL, 0, 0); + do_strtosz(" \t ", -EINVAL, 0, 0); + do_strtosz(".", -EINVAL, 0, 0); + do_strtosz(" .", -EINVAL, 0, 0); + do_strtosz(" .k", -EINVAL, 0, 0); + do_strtosz("inf", -EINVAL, 0, 0); + do_strtosz("NaN", -EINVAL, 0, 0); /* Lone suffix is not okay */ - do_strtosz("k", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" M", -EINVAL, 0xbaadf00d, 0); + do_strtosz("k", -EINVAL, 0, 0); + do_strtosz(" M", -EINVAL, 0, 0); /* Fractional values require scale larger than bytes */ - do_strtosz("1.1B", -EINVAL, 0xbaadf00d, 0); - do_strtosz("1.1", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1B", -EINVAL, 0, 0); + do_strtosz("1.1", -EINVAL, 0, 0); /* FIXME underflow in the fraction tail should matter for 'B' */ - do_strtosz("1.00001B", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.00001B", -EINVAL, 0, 0); do_strtosz("1.00000000000000000001B", 0 /* FIXME -EINVAL */, - 1 /* FIXME 0xbaadf00d */, 23 /* FIXME 0 */); + 1 /* FIXME 0 */, 23 /* FIXME 0 */); do_strtosz("1." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3452,62 +3452,60 @@ static void test_qemu_strtosz_invalid(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */, + "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */, 354 /* FIXME 0 */); /* No hex fractions */ - do_strtosz("0x1.8k", -EINVAL, 0xbaadf00d, 0); - do_strtosz("0x1.k", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x1.8k", -EINVAL, 0, 0); + do_strtosz("0x1.k", -EINVAL, 0, 0); /* No hex suffixes */ - do_strtosz("0x18M", -EINVAL, 0xbaadf00d, 0); - do_strtosz("0x1p1", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x18M", -EINVAL, 0, 0); + do_strtosz("0x1p1", -EINVAL, 0, 0); /* decimal in place of scaling suffix */ - do_strtosz("1.1.k", -EINVAL, 0xbaadf00d, 0); - do_strtosz("1.1.", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1.k", -EINVAL, 0, 0); + do_strtosz("1.1.", -EINVAL, 0, 0); } static void test_qemu_strtosz_trailing(void) { /* Trailing whitespace */ - do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0); /* Unknown suffix overrides even implied scale*/ - do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); + do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0); /* Implied scale allows partial parse */ - do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, - -EINVAL, 0xbaadf00d); - do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, - -EINVAL, 0xbaadf00d); + do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, -EINVAL, 0); + do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, -EINVAL, 0); /* Junk after one-byte suffix */ - do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0); /* Incomplete hex is an unknown suffix */ - do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0xbaadf00d); + do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0); /* Hex literals use only one leading zero */ - do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0); /* No support for binary literals; 'b' is valid suffix */ - do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0); /* Junk after decimal */ - do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0); /* Although negatives are invalid, '-' may be in trailing junk */ - do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); - do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0xbaadf00d); + do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0); + do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0); /* FIXME should stop parse after 'e'. No floating point exponents */ do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0xbaadf00d); + 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0); do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0xbaadf00d); + 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0); /* * FIXME overflow in fraction is so buggy it can read beyond bounds @@ -3515,20 +3513,19 @@ static void test_qemu_strtosz_trailing(void) */ do_strtosz_full("1.5E999\0\0" /* FIXME 1.5E999" */, qemu_strtosz, 0, 1 /* FIXME EiB * 1.5 */, 8 /* FIXME 4 */, - 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */); + 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */); } static void test_qemu_strtosz_erange(void) { /* FIXME negative values fit better as ERANGE */ - do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 3 */); - do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 2 */); - do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, - 0xbaadf00d, 0 /* FIXME 2 */, -EINVAL, 0xbaadf00d); - do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, - 0 /* FIXME 4 */); - do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, - 0xbaadf00d, 0 /* FIXME 3 */, -EINVAL, 0xbaadf00d); + do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 3 */); + do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 2 */); + do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, + 0 /* FIXME 2 */, -EINVAL, 0); + do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 4 */); + do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, + 0 /* FIXME 3 */, -EINVAL, 0); do_strtosz_full(" -." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3537,21 +3534,20 @@ static void test_qemu_strtosz_erange(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, - 0xbaadf00d, 0 /* FIXME 354 */, -EINVAL, 0xbaadf00d); + "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, + 0 /* FIXME 354 */, -EINVAL, 0); /* 2^64; see strtosz_simple for 2^64-1 */ - do_strtosz("18446744073709551616", -ERANGE, 0xbaadf00d, 20); + do_strtosz("18446744073709551616", -ERANGE, 0, 20); - do_strtosz("20E", -ERANGE, 0xbaadf00d, 3); + do_strtosz("20E", -ERANGE, 0, 3); /* FIXME Fraction tail can cause ERANGE overflow */ do_strtosz("15.9999999999999999999999999999999999999999999999999999E", - 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0xbaadf00d */, 56); + 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0 */, 56); /* EINVAL has priority over ERANGE */ - do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0xbaadf00d, 7, - -EINVAL, 0xbaadf00d); + do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0, 7, -EINVAL, 0); } static void test_qemu_strtosz_metric(void) diff --git a/util/cutils.c b/util/cutils.c index 1dc67d201d..c5530a5c2b 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -205,13 +205,15 @@ static int64_t suffix_mul(char suffix, int64_t unit) * * The end pointer will be returned in *end, if not NULL. If there is * no fraction, the input can be decimal or hexadecimal; if there is a - * fraction, then the input must be decimal and there must be a suffix - * (possibly by @default_suffix) larger than Byte, and the fractional - * portion may suffer from precision loss or rounding. The input must - * be positive. + * non-zero fraction, then the input must be decimal and there must be + * a suffix (possibly by @default_suffix) larger than Byte, and the + * fractional portion may suffer from precision loss or rounding. The + * input must be positive. * * Return -ERANGE on overflow (with *@end advanced), and -EINVAL on - * other error (with *@end left unchanged). + * other error (with *@end at @nptr). Unlike strtoull, *@result is + * set to 0 on all errors, as returning UINT64_MAX on overflow is less + * likely to be usable as a size. */ static int do_strtosz(const char *nptr, const char **end, const char default_suffix, int64_t unit, @@ -311,6 +313,11 @@ out: } if (retval == 0) { *result = val; + } else { + *result = 0; + if (end && retval == -EINVAL) { + *end = nptr; + } } return retval; From 3c5f2467984c23aae5a64548dcb15efae18e207e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:38 -0500 Subject: [PATCH 028/412] cutils: Set value in all integral qemu_strto* error paths Our goal in writing qemu_strtoi() and friends is to have an interface harder to abuse than libc's strtol(). Leaving the return value uninitialized on some but not all error paths does not lend itself well to this goal; and our documentation wasn't helpful on what to expect. Note that the previous patch changed all qemu_strtosz() EINVAL error paths to slam value to 0 rather than stay uninitialized, even when the EINVAL eror occurs because of trailing junk. But for the remaining integral qemu_strto*, it's easier to return the parsed value than to force things back to zero, in part because of how check_strtox_error works; in part because people expect that from libc strto* (while there is no libc strtosz to compare to), and in part because doing so creates less churn in the testsuite. Here, the list of affected callers is much longer ('git grep "qemu_strto[ui]" "*.c" "**/*.c" | grep -v tests/ |wc -l' outputs 107, although a few of those are the implementation in in cutils.c), so touching as little as possible is the wisest course of action. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-17-eblake@redhat.com> --- tests/unit/test-cutils.c | 24 +++++++++++------------ util/cutils.c | 42 +++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index f4dfb6ae14..cfe1fc8a17 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -320,7 +320,7 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -661,7 +661,7 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -764,7 +764,7 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); g_assert_null(endptr); } @@ -1102,7 +1102,7 @@ static void test_qemu_strtoui_full_null(void) err = qemu_strtoui(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtoui_full_empty(void) @@ -1202,7 +1202,7 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -1516,7 +1516,7 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -1619,7 +1619,7 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); g_assert_null(endptr); } @@ -1932,7 +1932,7 @@ static void test_qemu_strtoul_full_null(void) err = qemu_strtoul(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtoul_full_empty(void) @@ -2032,7 +2032,7 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -2322,7 +2322,7 @@ static void test_qemu_strtoi64_full_null(void) err = qemu_strtoi64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi64_full_empty(void) @@ -2425,7 +2425,7 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); g_assert_null(endptr); } @@ -2714,7 +2714,7 @@ static void test_qemu_strtou64_full_null(void) err = qemu_strtou64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtou64_full_empty(void) diff --git a/util/cutils.c b/util/cutils.c index c5530a5c2b..edfb71a217 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -384,12 +384,13 @@ static int check_strtox_error(const char *nptr, char *ep, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store INT_MAX in @result, * and return -ERANGE. @@ -410,6 +411,7 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -439,12 +441,13 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store UINT_MAX in @result, * and return -ERANGE. @@ -465,6 +468,7 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -508,12 +512,13 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store LONG_MAX in @result, * and return -ERANGE. @@ -530,6 +535,7 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -550,12 +556,13 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store ULONG_MAX in @result, * and return -ERANGE. @@ -573,6 +580,7 @@ int qemu_strtoul(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -601,6 +609,7 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -628,6 +637,7 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } From b87ac96651054fa89baab4e3a88a7feee7f92314 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:39 -0500 Subject: [PATCH 029/412] cutils: Use parse_uint in qemu_strtosz for negative rejection Rather than open-coding two different ways to check for an unwanted negative sign, reuse the same code in both functions. That way, if we decide down the road to accept "-0" instead of rejecting it, we have fewer places to change. Also, it means we now get ERANGE instead of EINVAL for negative values in qemu_strtosz, which is reasonable for what it represents. This in turn changes the expected output of a couple of iotests. The change is not quite complete: negative fractional scaled values can trip us up. This will be fixed in a later patch addressing other issues with fractional scaled values. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-18-eblake@redhat.com> --- tests/qemu-iotests/049.out | 7 ++----- tests/qemu-iotests/178.out.qcow2 | 3 +-- tests/qemu-iotests/178.out.raw | 3 +-- tests/unit/test-cutils.c | 7 +++---- util/cutils.c | 8 ++------ 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 8719c91b48..34e1b452e6 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -92,13 +92,10 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp == 3. Invalid sizes == qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 -qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 -Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- -and exabytes, respectively. +qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size' qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index 0d51fe401e..fe193fd5f4 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -13,8 +13,7 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 116241ddef..445e460fad 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -13,8 +13,7 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index cfe1fc8a17..0e3215a46e 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3519,10 +3519,9 @@ static void test_qemu_strtosz_trailing(void) static void test_qemu_strtosz_erange(void) { /* FIXME negative values fit better as ERANGE */ - do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 3 */); - do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 2 */); - do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, - 0 /* FIXME 2 */, -EINVAL, 0); + do_strtosz(" -0", -ERANGE, 0, 3); + do_strtosz("-1", -ERANGE, 0, 2); + do_strtosz_full("-2M", qemu_strtosz, -ERANGE, 0, 2, -EINVAL, 0); do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 4 */); do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 3 */, -EINVAL, 0); diff --git a/util/cutils.c b/util/cutils.c index edfb71a217..e3a49209a9 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -201,6 +201,7 @@ static int64_t suffix_mul(char suffix, int64_t unit) * - hex with scaling suffix, such as 0x20M * - octal, such as 08 * - fractional hex, such as 0x1.8 + * - negative values, including -0 * - floating point exponents, such as 1e3 * * The end pointer will be returned in *end, if not NULL. If there is @@ -226,15 +227,10 @@ static int do_strtosz(const char *nptr, const char **end, int64_t mul; /* Parse integral portion as decimal. */ - retval = qemu_strtou64(nptr, &endptr, 10, &val); + retval = parse_uint(nptr, &endptr, 10, &val); if (retval) { goto out; } - if (memchr(nptr, '-', endptr - nptr) != NULL) { - endptr = nptr; - retval = -EINVAL; - goto out; - } if (val == 0 && (*endptr == 'x' || *endptr == 'X')) { /* Input looks like hex; reparse, and insist on no fraction or suffix. */ retval = qemu_strtou64(nptr, &endptr, 16, &val); From c25b1683443c6d658a82dc1c5587fdb0ae81663c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:40 -0500 Subject: [PATCH 030/412] cutils: Improve qemu_strtod* error paths Previous patches changed all integral qemu_strto*() error paths to guarantee that *value is never left uninitialized. Do likewise for qemu_strtod. Also, tighten qemu_strtod_finite() to never return a non-finite value (prior to this patch, we were rejecting "inf" with -EINVAL and unspecified result 0.0, but failing "9e999" with -ERANGE and HUGE_VAL - which is infinite on IEEE machines - despite our function claiming to recognize only finite values). Auditing callers, we have no external callers of qemu_strtod, and among the callers of qemu_strtod_finite: - qapi/qobject-input-visitor.c:qobject_input_type_number_keyval() and qapi/string-input-visitor.c:parse_type_number() which reject all errors (does not matter what we store) - utils/cutils.c:do_strtosz() incorrectly assumes that *endptr points to '.' on all failures (that is, it is not distinguishing between EINVAL and ERANGE; and therefore still does the WRONG THING for "9.9e999". The change here does not entirely fix that (a later patch will tackle this more systematically), but at least it fixes the read-out-of-bounds first diagnosed in https://gitlab.com/qemu-project/qemu/-/issues/1629 - our testsuite, which we can update to match what we document Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek CC: qemu-stable@nongnu.org Message-Id: <20230522190441.64278-19-eblake@redhat.com> --- tests/unit/test-cutils.c | 63 +++++++++++++++++++++++----------------- util/cutils.c | 32 +++++++++++--------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 0e3215a46e..1ee410fae4 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -2868,7 +2868,8 @@ static void test_qemu_strtod_einval(void) res = 999; err = qemu_strtod(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_null(endptr); /* not recognizable */ @@ -3101,7 +3102,8 @@ static void test_qemu_strtod_finite_einval(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); /* NULL */ @@ -3110,7 +3112,8 @@ static void test_qemu_strtod_finite_einval(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_null(endptr); /* not recognizable */ @@ -3119,7 +3122,8 @@ static void test_qemu_strtod_finite_einval(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); } @@ -3130,24 +3134,26 @@ static void test_qemu_strtod_finite_erange(void) int err; double res; - /* overflow */ + /* overflow turns into EINVAL */ str = "9e999"; endptr = "somewhere"; res = 999; err = qemu_strtod_finite(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpfloat(res, ==, HUGE_VAL); - g_assert_true(endptr == str + 5); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); str = "-9e+999"; endptr = "somewhere"; res = 999; err = qemu_strtod_finite(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpfloat(res, ==, -HUGE_VAL); - g_assert_true(endptr == str + 7); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); - /* underflow */ + /* underflow is still possible */ str = "-9e-999"; endptr = "somewhere"; res = 999; @@ -3172,7 +3178,8 @@ static void test_qemu_strtod_finite_nonfinite(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); str = "-infinity"; @@ -3180,7 +3187,8 @@ static void test_qemu_strtod_finite_nonfinite(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); /* not a number */ @@ -3189,7 +3197,8 @@ static void test_qemu_strtod_finite_nonfinite(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); } @@ -3213,7 +3222,8 @@ static void test_qemu_strtod_finite_trailing(void) res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_false(signbit(res)); /* trailing e is not an exponent */ str = ".5e"; @@ -3228,7 +3238,7 @@ static void test_qemu_strtod_finite_trailing(void) res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.5); /* trailing ( not part of long NaN */ str = "nan("; @@ -3236,14 +3246,16 @@ static void test_qemu_strtod_finite_trailing(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); endptr = "somewhere"; res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); } static void test_qemu_strtod_finite_erange_junk(void) @@ -3269,7 +3281,8 @@ static void test_qemu_strtod_finite_erange_junk(void) res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); } typedef int (*qemu_strtosz_fn)(const char *, const char **, uint64_t *); @@ -3507,13 +3520,9 @@ static void test_qemu_strtosz_trailing(void) 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, -EINVAL, 0); - /* - * FIXME overflow in fraction is so buggy it can read beyond bounds - * if we don't stuff extra \0 in our literal - */ - do_strtosz_full("1.5E999\0\0" /* FIXME 1.5E999" */, qemu_strtosz, - 0, 1 /* FIXME EiB * 1.5 */, 8 /* FIXME 4 */, - 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */); + /* FIXME overflow in fraction is still buggy */ + do_strtosz_full("1.5E999", qemu_strtosz, 0, 1 /* FIXME EiB * 1.5 */, + 2 /* FIXME 4 */, -EINVAL, 0); } static void test_qemu_strtosz_erange(void) diff --git a/util/cutils.c b/util/cutils.c index e3a49209a9..bde2da59bd 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -660,12 +660,13 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, +0.0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows, store +/-HUGE_VAL in @result, depending * on the sign, and return -ERANGE. @@ -680,6 +681,7 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result) char *ep; if (!nptr) { + *result = 0.0; if (endptr) { *endptr = nptr; } @@ -694,24 +696,28 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result) /** * Convert string @nptr to a finite double. * - * Works like qemu_strtod(), except that "NaN" and "inf" are rejected - * with -EINVAL and no conversion is performed. + * Works like qemu_strtod(), except that "NaN", "inf", and strings + * that cause ERANGE overflow errors are rejected with -EINVAL as if + * no conversion is performed, storing 0.0 into @result regardless of + * any sign. -ERANGE failures for underflow still preserve the parsed + * sign. */ int qemu_strtod_finite(const char *nptr, const char **endptr, double *result) { - double tmp; + const char *tmp; int ret; - ret = qemu_strtod(nptr, endptr, &tmp); - if (!ret && !isfinite(tmp)) { + ret = qemu_strtod(nptr, &tmp, result); + if (!isfinite(*result)) { if (endptr) { *endptr = nptr; } + *result = 0.0; + ret = -EINVAL; + } else if (endptr) { + *endptr = tmp; + } else if (*tmp) { ret = -EINVAL; - } - - if (ret != -EINVAL) { - *result = tmp; } return ret; } From 42cc08d13ab8e68f76882b216da0b28d06f29e11 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:41 -0500 Subject: [PATCH 031/412] cutils: Improve qemu_strtosz handling of fractions We have several limitations and bugs worth fixing; they are inter-related enough that it is not worth splitting this patch into smaller pieces: * ".5k" should work to specify 512, just as "0.5k" does * "1.9999k" and "1." + "9"*50 + "k" should both produce the same result of 2048 after rounding * "1." + "0"*350 + "1B" should not be treated the same as "1.0B"; underflow in the fraction should not be lost * "7.99e99" and "7.99e999" look similar, but our code was doing a read-out-of-bounds on the latter because it was not expecting ERANGE due to overflow. While we document that scientific notation is not supported, and the previous patch actually fixed qemu_strtod_finite() to no longer return ERANGE overflows, it is easier to pre-filter than to try and determine after the fact if strtod() consumed more than we wanted. Note that this is a low-level semantic change (when endptr is not NULL, we can now successfully parse with a scale of 'E' and then report trailing junk, instead of failing outright with EINVAL); but an earlier commit already argued that this is not a high-level semantic change since the only caller passing in a non-NULL endptr also checks that the tail is whitespace-only. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1629 Fixes: cf923b78 ("utils: Improve qemu_strtosz() to have 64 bits of precision", 6.0.0) Fixes: 7625a1ed ("utils: Use fixed-point arithmetic in qemu_strtosz", 6.0.0) Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-20-eblake@redhat.com> [eblake: tweak function comment for accuracy] --- tests/unit/test-cutils.c | 50 +++++++++------------- util/cutils.c | 92 ++++++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 54 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 1ee410fae4..227acc5995 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3408,19 +3408,18 @@ static void test_qemu_strtosz_float(void) /* An empty fraction tail is tolerated */ do_strtosz("1.k", 0, 1024, 3); - /* FIXME An empty fraction head should be tolerated */ - do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0 /* FIXME 512 */, - 0 /* FIXME 4 */); + /* An empty fraction head is tolerated */ + do_strtosz(" .5k", 0, 512, 4); /* For convenience, we permit values that are not byte-exact */ do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); - /* FIXME Fraction tail should round correctly */ + /* Fraction tail can round up */ do_strtosz("1.9999k", 0, 2048, 7); do_strtosz("1.9999999999999999999999999999999999999999999999999999k", 0, - 1024 /* FIXME 2048 */, 55); + 2048, 55); - /* FIXME ERANGE underflow in the fraction tail should not matter for 'k' */ + /* ERANGE underflow in the fraction tail does not matter for 'k' */ do_strtosz("1." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3429,7 +3428,7 @@ static void test_qemu_strtosz_float(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1k", 0, 1 /* FIXME 1024 */, 354); + "1k", 0, 1024, 354); } static void test_qemu_strtosz_invalid(void) @@ -3453,10 +3452,9 @@ static void test_qemu_strtosz_invalid(void) do_strtosz("1.1B", -EINVAL, 0, 0); do_strtosz("1.1", -EINVAL, 0, 0); - /* FIXME underflow in the fraction tail should matter for 'B' */ + /* 'B' cannot have any nonzero fraction, even with rounding or underflow */ do_strtosz("1.00001B", -EINVAL, 0, 0); - do_strtosz("1.00000000000000000001B", 0 /* FIXME -EINVAL */, - 1 /* FIXME 0 */, 23 /* FIXME 0 */); + do_strtosz("1.00000000000000000001B", -EINVAL, 0, 0); do_strtosz("1." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3465,8 +3463,7 @@ static void test_qemu_strtosz_invalid(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */, - 354 /* FIXME 0 */); + "1B", -EINVAL, 0, 0); /* No hex fractions */ do_strtosz("0x1.8k", -EINVAL, 0, 0); @@ -3512,28 +3509,20 @@ static void test_qemu_strtosz_trailing(void) do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0); do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0); - /* FIXME should stop parse after 'e'. No floating point exponents */ - do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0); - do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0); - - /* FIXME overflow in fraction is still buggy */ - do_strtosz_full("1.5E999", qemu_strtosz, 0, 1 /* FIXME EiB * 1.5 */, - 2 /* FIXME 4 */, -EINVAL, 0); + /* Parse stops at 'e', which is not a floating point exponent */ + do_strtosz_full("1.5e1k", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); + do_strtosz_full("1.5E+0k", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); + do_strtosz_full("1.5E999", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); } static void test_qemu_strtosz_erange(void) { - /* FIXME negative values fit better as ERANGE */ + /* no negative values */ do_strtosz(" -0", -ERANGE, 0, 3); do_strtosz("-1", -ERANGE, 0, 2); do_strtosz_full("-2M", qemu_strtosz, -ERANGE, 0, 2, -EINVAL, 0); - do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 4 */); - do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, - 0 /* FIXME 3 */, -EINVAL, 0); + do_strtosz(" -.0", -ERANGE, 0, 4); + do_strtosz_full("-.1k", qemu_strtosz, -ERANGE, 0, 3, -EINVAL, 0); do_strtosz_full(" -." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3542,17 +3531,16 @@ static void test_qemu_strtosz_erange(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, - 0 /* FIXME 354 */, -EINVAL, 0); + "1M", qemu_strtosz, -ERANGE, 0, 354, -EINVAL, 0); /* 2^64; see strtosz_simple for 2^64-1 */ do_strtosz("18446744073709551616", -ERANGE, 0, 20); do_strtosz("20E", -ERANGE, 0, 3); - /* FIXME Fraction tail can cause ERANGE overflow */ + /* Fraction tail can cause ERANGE overflow */ do_strtosz("15.9999999999999999999999999999999999999999999999999999E", - 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0 */, 56); + -ERANGE, 0, 56); /* EINVAL has priority over ERANGE */ do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0, 7, -EINVAL, 0); diff --git a/util/cutils.c b/util/cutils.c index bde2da59bd..25373198ad 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -194,15 +194,19 @@ static int64_t suffix_mul(char suffix, int64_t unit) * - 12345 - decimal, scale determined by @default_suffix and @unit * - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit * - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and - * fractional portion is truncated to byte + * fractional portion is truncated to byte, either side of . may be empty * - 0x7fEE - hexadecimal, unit determined by @default_suffix * * The following are intentionally not supported - * - hex with scaling suffix, such as 0x20M - * - octal, such as 08 - * - fractional hex, such as 0x1.8 - * - negative values, including -0 - * - floating point exponents, such as 1e3 + * - hex with scaling suffix, such as 0x20M or 0x1p3 (both fail with + * -EINVAL), while 0x1b is 27 (not 1 with byte scale) + * - octal, such as 08 (parsed as decimal instead) + * - binary, such as 0b1000 (parsed as 0b with trailing garbage "1000") + * - fractional hex, such as 0x1.8 (parsed as 0 with trailing garbage "x1.8") + * - negative values, including -0 (fail with -ERANGE) + * - floating point exponents, such as 1e3 (parsed as 1e with trailing + * garbage "3") or 0x1p3 (rejected as hex with scaling suffix) + * - non-finite values, such as inf or NaN (fail with -EINVAL) * * The end pointer will be returned in *end, if not NULL. If there is * no fraction, the input can be decimal or hexadecimal; if there is a @@ -221,17 +225,17 @@ static int do_strtosz(const char *nptr, const char **end, uint64_t *result) { int retval; - const char *endptr, *f; + const char *endptr; unsigned char c; - uint64_t val, valf = 0; + uint64_t val = 0, valf = 0; int64_t mul; /* Parse integral portion as decimal. */ retval = parse_uint(nptr, &endptr, 10, &val); - if (retval) { + if (retval == -ERANGE || !nptr) { goto out; } - if (val == 0 && (*endptr == 'x' || *endptr == 'X')) { + if (retval == 0 && val == 0 && (*endptr == 'x' || *endptr == 'X')) { /* Input looks like hex; reparse, and insist on no fraction or suffix. */ retval = qemu_strtou64(nptr, &endptr, 16, &val); if (retval) { @@ -242,26 +246,68 @@ static int do_strtosz(const char *nptr, const char **end, retval = -EINVAL; goto out; } - } else if (*endptr == '.') { + } else if (*endptr == '.' || (endptr == nptr && strchr(nptr, '.'))) { /* * Input looks like a fraction. Make sure even 1.k works - * without fractional digits. If we see an exponent, treat - * the entire input as invalid instead. + * without fractional digits. strtod tries to treat 'e' as an + * exponent, but we want to treat it as a scaling suffix; + * doing this requires modifying a copy of the fraction. */ - double fraction; + double fraction = 0.0; - f = endptr; - retval = qemu_strtod_finite(f, &endptr, &fraction); - if (retval) { + if (retval == 0 && *endptr == '.' && !isdigit(endptr[1])) { + /* If we got here, we parsed at least one digit already. */ endptr++; - } else if (memchr(f, 'e', endptr - f) || memchr(f, 'E', endptr - f)) { - endptr = nptr; - retval = -EINVAL; - goto out; } else { - /* Extract into a 64-bit fixed-point fraction. */ - valf = (uint64_t)(fraction * 0x1p64); + char *e; + const char *tail; + g_autofree char *copy = g_strdup(endptr); + + e = strchr(copy, 'e'); + if (e) { + *e = '\0'; + } + e = strchr(copy, 'E'); + if (e) { + *e = '\0'; + } + /* + * If this is a floating point, we are guaranteed that '.' + * appears before any possible digits in copy. If it is + * not a floating point, strtod will fail. Either way, + * there is now no exponent in copy, so if it parses, we + * know 0.0 <= abs(result) <= 1.0 (after rounding), and + * ERANGE is only possible on underflow which is okay. + */ + retval = qemu_strtod_finite(copy, &tail, &fraction); + endptr += tail - copy; + if (signbit(fraction)) { + retval = -ERANGE; + goto out; + } } + + /* Extract into a 64-bit fixed-point fraction. */ + if (fraction == 1.0) { + if (val == UINT64_MAX) { + retval = -ERANGE; + goto out; + } + val++; + } else if (retval == -ERANGE) { + /* See comments above about underflow */ + valf = 1; + retval = 0; + } else { + /* We want non-zero valf for any non-zero fraction */ + valf = (uint64_t)(fraction * 0x1p64); + if (valf == 0 && fraction > 0.0) { + valf = 1; + } + } + } + if (retval) { + goto out; } c = *endptr; mul = suffix_mul(c, unit); From 8555ddc671203969b0e6eb651e538d02a9a79b3a Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Sun, 21 May 2023 11:23:04 +0100 Subject: [PATCH 032/412] hw/intc/loongarch_ipi: Bring back all 4 IPI mailboxes As per "Loongson 3A5000/3B5000 Processor Reference Manual", Loongson 3A5000's IPI implementation have 4 mailboxes per core. However, in 78464f023b54 ("hw/loongarch/virt: Modify ipi as percpu device"), the number of IPI mailboxes was reduced to one, which mismatches actual hardware. It won't affect LoongArch based system as LoongArch boot code only uses the first mailbox, however MIPS based Loongson boot code uses all 4 mailboxes. Fixes Coverity CID: 1512452, 1512453 Fixes: 78464f023b54 ("hw/loongarch/virt: Modify ipi as percpu device") Signed-off-by: Jiaxun Yang Reviewed-by: Song Gao Message-Id: <20230521102307.87081-2-jiaxun.yang@flygoat.com> Signed-off-by: Song Gao --- hw/intc/loongarch_ipi.c | 6 +++--- include/hw/intc/loongarch_ipi.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index d6ab91721e..3e45381652 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -238,14 +238,14 @@ static void loongarch_ipi_init(Object *obj) static const VMStateDescription vmstate_ipi_core = { .name = "ipi-single", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(status, IPICore), VMSTATE_UINT32(en, IPICore), VMSTATE_UINT32(set, IPICore), VMSTATE_UINT32(clear, IPICore), - VMSTATE_UINT32_ARRAY(buf, IPICore, 2), + VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 664e050b92..6c6194786e 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -28,6 +28,8 @@ #define MAIL_SEND_OFFSET 0 #define ANY_SEND_OFFSET (IOCSR_ANY_SEND - IOCSR_MAIL_SEND) +#define IPI_MBX_NUM 4 + #define TYPE_LOONGARCH_IPI "loongarch_ipi" OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI) @@ -37,7 +39,7 @@ typedef struct IPICore { uint32_t set; uint32_t clear; /* 64bit buf divide into 2 32bit buf */ - uint32_t buf[2]; + uint32_t buf[IPI_MBX_NUM * 2]; qemu_irq irq; } IPICore; From 079181b9bc60389e106009a1530d3cc42256f567 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 26 May 2023 20:12:37 +0200 Subject: [PATCH 033/412] target/s390x: Fix LCBB overwriting the top 32 bits LCBB is supposed to overwrite only the bottom 32 bits, but QEMU erroneously overwrites the entire register. Fixes: 6d9303322ed9 ("s390x/tcg: Implement LOAD COUNT TO BLOCK BOUNDARY") Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Message-Id: <20230526181240.1425579-2-iii@linux.ibm.com> Reviewed-by: Richard Henderson Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- target/s390x/tcg/insn-data.h.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index bcc70d99ba..e41672684a 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -486,7 +486,7 @@ F(0xb343, LCXBR, RRE, Z, x2h, x2l, new_P, x1_P, negf128, f128, IF_BFP) F(0xb373, LCDFR, RRE, FPSSH, 0, f2, new, f1, negf64, 0, IF_AFP1 | IF_AFP2) /* LOAD COUNT TO BLOCK BOUNDARY */ - C(0xe727, LCBB, RXE, V, la2, 0, r1, 0, lcbb, 0) + C(0xe727, LCBB, RXE, V, la2, 0, new, r1_32, lcbb, 0) /* LOAD HALFWORD */ C(0xb927, LHR, RRE, EI, 0, r2_16s, 0, r1_32, mov2, 0) C(0xb907, LGHR, RRE, EI, 0, r2_16s, 0, r1, mov2, 0) From 05d000fb4dcac4bc02ffa08fcf14b51683b878f6 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 26 May 2023 20:12:38 +0200 Subject: [PATCH 034/412] tests/tcg/s390x: Test LCBB Add a test to prevent regressions. Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Message-Id: <20230526181240.1425579-3-iii@linux.ibm.com> Reviewed-by: David Hildenbrand Acked-by: Richard Henderson Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/lcbb.c | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/tcg/s390x/lcbb.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 23dc8b6a63..4ed07c6ab0 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -47,6 +47,7 @@ $(PGM_SPECIFICATION_TESTS): LDFLAGS+=pgm-specification-user.o TESTS += $(PGM_SPECIFICATION_TESTS) Z13_TESTS=vistr +Z13_TESTS+=lcbb $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) diff --git a/tests/tcg/s390x/lcbb.c b/tests/tcg/s390x/lcbb.c new file mode 100644 index 0000000000..8d368e0998 --- /dev/null +++ b/tests/tcg/s390x/lcbb.c @@ -0,0 +1,51 @@ +/* + * Test the LCBB instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static inline __attribute__((__always_inline__)) void +lcbb(long *r1, void *dxb2, int m3, int *cc) +{ + asm("lcbb %[r1],%[dxb2],%[m3]\n" + "ipm %[cc]" + : [r1] "+r" (*r1), [cc] "=r" (*cc) + : [dxb2] "R" (*(char *)dxb2), [m3] "i" (m3) + : "cc"); + *cc = (*cc >> 28) & 3; +} + +static char buf[0x1000] __attribute__((aligned(0x1000))); + +static inline __attribute__((__always_inline__)) void +test_lcbb(void *p, int m3, int exp_r1, int exp_cc) +{ + long r1 = 0xfedcba9876543210; + int cc; + + lcbb(&r1, p, m3, &cc); + assert(r1 == (0xfedcba9800000000 | exp_r1)); + assert(cc == exp_cc); +} + +int main(void) +{ + test_lcbb(&buf[0], 0, 16, 0); + test_lcbb(&buf[63], 0, 1, 3); + test_lcbb(&buf[0], 1, 16, 0); + test_lcbb(&buf[127], 1, 1, 3); + test_lcbb(&buf[0], 2, 16, 0); + test_lcbb(&buf[255], 2, 1, 3); + test_lcbb(&buf[0], 3, 16, 0); + test_lcbb(&buf[511], 3, 1, 3); + test_lcbb(&buf[0], 4, 16, 0); + test_lcbb(&buf[1023], 4, 1, 3); + test_lcbb(&buf[0], 5, 16, 0); + test_lcbb(&buf[2047], 5, 1, 3); + test_lcbb(&buf[0], 6, 16, 0); + test_lcbb(&buf[4095], 6, 1, 3); + + return EXIT_SUCCESS; +} From 3180b173621021c365c256cedf2f5845bd4780d0 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 26 May 2023 20:12:39 +0200 Subject: [PATCH 035/412] target/s390x: Fix LOCFHR taking the wrong half of R2 LOCFHR should write top-to-top, but QEMU erroneously writes bottom-to-top. Fixes: 45aa9aa3b773 ("target/s390x: Implement load-on-condition-2 insns") Cc: qemu-stable@nongnu.org Reported-by: Mikhail Mitskevich Closes: https://gitlab.com/qemu-project/qemu/-/issues/1668 Signed-off-by: Ilya Leoshkevich Message-Id: <20230526181240.1425579-4-iii@linux.ibm.com> Reviewed-by: Richard Henderson Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- target/s390x/tcg/insn-data.h.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index e41672684a..937e18ea9d 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -564,7 +564,7 @@ C(0xec46, LOCGHI, RIE_g, LOC2, r1, i2, r1, 0, loc, 0) C(0xec4e, LOCHHI, RIE_g, LOC2, r1_sr32, i2, new, r1_32h, loc, 0) /* LOAD HIGH ON CONDITION */ - C(0xb9e0, LOCFHR, RRF_c, LOC2, r1_sr32, r2, new, r1_32h, loc, 0) + C(0xb9e0, LOCFHR, RRF_c, LOC2, r1_sr32, r2_sr32, new, r1_32h, loc, 0) C(0xebe0, LOCFH, RSY_b, LOC2, r1_sr32, m2_32u, new, r1_32h, loc, 0) /* LOAD PAIR DISJOINT */ D(0xc804, LPD, SSF, ILA, 0, 0, new_P, r3_P32, lpd, 0, MO_TEUL) From 230976232f4fcdc205d6ec53ec9f3804b28dc1e7 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 26 May 2023 20:12:40 +0200 Subject: [PATCH 036/412] tests/tcg/s390x: Test LOCFHR Add a small test to prevent regressions. Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Message-Id: <20230526181240.1425579-5-iii@linux.ibm.com> Reviewed-by: Richard Henderson Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/locfhr.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/tcg/s390x/locfhr.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 4ed07c6ab0..b14c0bd84b 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -48,6 +48,7 @@ TESTS += $(PGM_SPECIFICATION_TESTS) Z13_TESTS=vistr Z13_TESTS+=lcbb +Z13_TESTS+=locfhr $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) diff --git a/tests/tcg/s390x/locfhr.c b/tests/tcg/s390x/locfhr.c new file mode 100644 index 0000000000..ab9ff6e449 --- /dev/null +++ b/tests/tcg/s390x/locfhr.c @@ -0,0 +1,29 @@ +/* + * Test the LOCFHR instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static inline __attribute__((__always_inline__)) long +locfhr(long r1, long r2, int m3, int cc) +{ + cc <<= 28; + asm("spm %[cc]\n" + "locfhr %[r1],%[r2],%[m3]\n" + : [r1] "+r" (r1) + : [cc] "r" (cc), [r2] "r" (r2), [m3] "i" (m3) + : "cc"); + return r1; +} + +int main(void) +{ + assert(locfhr(0x1111111122222222, 0x3333333344444444, 8, 0) == + 0x3333333322222222); + assert(locfhr(0x5555555566666666, 0x7777777788888888, 11, 1) == + 0x5555555566666666); + + return EXIT_SUCCESS; +} From 01b9990a3fb84bb9a14017255ab1a4fa86588215 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 11 May 2023 01:02:12 +0200 Subject: [PATCH 037/412] linux-user/s390x: Fix single-stepping SVC Currently single-stepping SVC executes two instructions. The reason is that EXCP_DEBUG for the SVC instruction itself is masked by EXCP_SVC. Fix by re-raising EXCP_DEBUG. Signed-off-by: Ilya Leoshkevich Message-Id: <20230510230213.330134-2-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- linux-user/s390x/cpu_loop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 285bc60071..8b7ac2879e 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -86,6 +86,15 @@ void cpu_loop(CPUS390XState *env) } else if (ret != -QEMU_ESIGRETURN) { env->regs[2] = ret; } + + if (unlikely(cs->singlestep_enabled)) { + /* + * cpu_tb_exec() did not raise EXCP_DEBUG, because it has seen + * that EXCP_SVC was already pending. + */ + cs->exception_index = EXCP_DEBUG; + } + break; case EXCP_DEBUG: From be4a4cb429617a8b6893733b37b6203e4b7bf35b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 11 May 2023 01:02:13 +0200 Subject: [PATCH 038/412] tests/tcg/s390x: Test single-stepping SVC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a small test to prevent regressions. Signed-off-by: Ilya Leoshkevich Acked-by: Alex Bennée Message-Id: <20230510230213.330134-3-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 11 ++++- tests/tcg/s390x/gdbstub/test-svc.py | 64 +++++++++++++++++++++++++++++ tests/tcg/s390x/hello-s390x-asm.S | 20 +++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/s390x/gdbstub/test-svc.py create mode 100644 tests/tcg/s390x/hello-s390x-asm.S diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index b14c0bd84b..a2a15c4a23 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -77,7 +77,16 @@ run-gdbstub-signals-s390x: signals-s390x --bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \ mixing signals and debugging) -EXTRA_RUNS += run-gdbstub-signals-s390x +hello-s390x-asm: CFLAGS+=-nostdlib + +run-gdbstub-svc: hello-s390x-asm + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(S390X_SRC)/gdbstub/test-svc.py, \ + single-stepping svc) + +EXTRA_RUNS += run-gdbstub-signals-s390x run-gdbstub-svc endif # MVX versions of sha512 diff --git a/tests/tcg/s390x/gdbstub/test-svc.py b/tests/tcg/s390x/gdbstub/test-svc.py new file mode 100644 index 0000000000..7851ca7284 --- /dev/null +++ b/tests/tcg/s390x/gdbstub/test-svc.py @@ -0,0 +1,64 @@ +"""Test single-stepping SVC. + +This runs as a sourced script (via -x, via run-test.py).""" +from __future__ import print_function +import gdb +import sys + + +n_failures = 0 + + +def report(cond, msg): + """Report success/fail of a test""" + if cond: + print("PASS: {}".format(msg)) + else: + print("FAIL: {}".format(msg)) + global n_failures + n_failures += 1 + + +def run_test(): + """Run through the tests one by one""" + report("lghi\t" in gdb.execute("x/i $pc", False, True), "insn #1") + gdb.execute("si") + report("larl\t" in gdb.execute("x/i $pc", False, True), "insn #2") + gdb.execute("si") + report("lghi\t" in gdb.execute("x/i $pc", False, True), "insn #3") + gdb.execute("si") + report("svc\t" in gdb.execute("x/i $pc", False, True), "insn #4") + gdb.execute("si") + report("xgr\t" in gdb.execute("x/i $pc", False, True), "insn #5") + gdb.execute("si") + report("svc\t" in gdb.execute("x/i $pc", False, True), "insn #6") + gdb.execute("si") + + +def main(): + """Prepare the environment and run through the tests""" + try: + inferior = gdb.selected_inferior() + print("ATTACHED: {}".format(inferior.architecture().name())) + except (gdb.error, AttributeError): + print("SKIPPING (not connected)") + exit(0) + + if gdb.parse_and_eval('$pc') == 0: + print("SKIP: PC not set") + exit(0) + + try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() + except gdb.error: + report(False, "GDB Exception: {}".format(sys.exc_info()[0])) + print("All tests complete: %d failures" % n_failures) + exit(n_failures) + + +main() diff --git a/tests/tcg/s390x/hello-s390x-asm.S b/tests/tcg/s390x/hello-s390x-asm.S new file mode 100644 index 0000000000..2e9faa1604 --- /dev/null +++ b/tests/tcg/s390x/hello-s390x-asm.S @@ -0,0 +1,20 @@ +/* + * Hello, World! in assembly. + */ + +.globl _start +_start: + +/* puts("Hello, World!"); */ +lghi %r2,1 +larl %r3,foo +lghi %r4,foo_end-foo +svc 4 + +/* exit(0); */ +xgr %r2,%r2 +svc 1 + +.align 2 +foo: .asciz "Hello, World!\n" +foo_end: From 0db0fbb5cf8955d4f7a4a82bde32cfd93bd042ea Mon Sep 17 00:00:00 2001 From: Max Fritz Date: Mon, 22 May 2023 02:12:02 +0200 Subject: [PATCH 039/412] Add conditional dependency for libkeyutils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This modification enables better control over the inclusion of libkeyutils based on the configuration, enhancing the flexibility of the build system. Signed-off-by: Max Fritz Message-Id: <168471463402.18155.3575359027429939965-1@git.sr.ht> Reviewed-by: Daniel P. Berrangé [thuth: Remove the "kwargs: static_kwargs" part - it's not necessary anymore] Signed-off-by: Thomas Huth --- meson.build | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index a61d3e9b06..576bc2fdbd 100644 --- a/meson.build +++ b/meson.build @@ -1781,8 +1781,10 @@ if gnutls.found() tasn1 = dependency('libtasn1', method: 'pkg-config') endif -keyutils = dependency('libkeyutils', required: false, - method: 'pkg-config') +keyutils = not_found +if get_option('keyring').enabled() + keyutils = dependency('libkeyutils', required: false, method: 'pkg-config') +endif has_gettid = cc.has_function('gettid') From 02520772ae1f5e336dd12765d7f9166e859642a9 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 23 May 2023 21:56:06 +0200 Subject: [PATCH 040/412] hw/timer/i8254_common: Share "iobase" property via base class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both TYPE_KVM_I8254 and TYPE_I8254 have their own but same implementation of the "iobase" property. The storage for the property already resides in PITCommonState, so also move the property definition there. Signed-off-by: Bernhard Beschow Acked-by: Michael S. Tsirkin Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230523195608.125820-2-shentey@gmail.com> Signed-off-by: Mark Cave-Ayland --- hw/i386/kvm/i8254.c | 1 - hw/timer/i8254.c | 6 ------ hw/timer/i8254_common.c | 6 ++++++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 191a26fa57..6a7383d877 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -301,7 +301,6 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) } static Property kvm_pit_properties[] = { - DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, lost_tick_policy, LOST_TICK_POLICY_DELAY), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index c8388ea432..c235496fc9 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -350,11 +350,6 @@ static void pit_realizefn(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -static Property pit_properties[] = { - DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), - DEFINE_PROP_END_OF_LIST(), -}; - static void pit_class_initfn(ObjectClass *klass, void *data) { PITClass *pc = PIT_CLASS(klass); @@ -366,7 +361,6 @@ static void pit_class_initfn(ObjectClass *klass, void *data) k->get_channel_info = pit_get_channel_info_common; k->post_load = pit_post_load; dc->reset = pit_reset; - device_class_set_props(dc, pit_properties); } static const TypeInfo pit_info = { diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c index 050875b497..e4093e2904 100644 --- a/hw/timer/i8254_common.c +++ b/hw/timer/i8254_common.c @@ -240,6 +240,11 @@ static const VMStateDescription vmstate_pit_common = { } }; +static Property pit_common_properties[] = { + DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), + DEFINE_PROP_END_OF_LIST(), +}; + static void pit_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -252,6 +257,7 @@ static void pit_common_class_init(ObjectClass *klass, void *data) * done by board code. */ dc->user_creatable = false; + device_class_set_props(dc, pit_common_properties); } static const TypeInfo pit_common_type = { From 14e066a7c440ce4c7c89ec119ba3b45b33f808d8 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 23 May 2023 21:56:07 +0200 Subject: [PATCH 041/412] hw/arm/omap: Remove unused omap_uart_attach() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is unused since commit bdad3654d3c55f478e538037d9eccd204e5fc8ee ('hw/arm/nseries: Remove invalid/unnecessary n8x0_uart_setup()'). Signed-off-by: Bernhard Beschow Acked-by: Michael S. Tsirkin Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230523195608.125820-3-shentey@gmail.com> Signed-off-by: Mark Cave-Ayland --- hw/char/omap_uart.c | 9 --------- include/hw/arm/omap.h | 1 - 2 files changed, 10 deletions(-) diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 1c890b9201..6848bddb4e 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -175,12 +175,3 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, return s; } - -void omap_uart_attach(struct omap_uart_s *s, Chardev *chr) -{ - /* TODO: Should reuse or destroy current s->serial */ - s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, - omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_new("null", "null", NULL), - DEVICE_NATIVE_ENDIAN); -} diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index c275d9b681..067e9419f7 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -724,7 +724,6 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, qemu_irq txdma, qemu_irq rxdma, const char *label, Chardev *chr); void omap_uart_reset(struct omap_uart_s *s); -void omap_uart_attach(struct omap_uart_s *s, Chardev *chr); struct omap_mpuio_s; qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); From 36c9189890bfb936b1b086da639e37fd92b50215 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 23 May 2023 21:56:08 +0200 Subject: [PATCH 042/412] hw/isa/i82378: Remove unused "io" attribute The attribute isn't used since commit 5c9736789b79ea49cd236ac326f0a414f63b1015 "i82378: Cleanup implementation". Signed-off-by: Bernhard Beschow Acked-by: Michael S. Tsirkin Reviewed-by: Mark Cave-Ayland Message-Id: <20230523195608.125820-4-shentey@gmail.com> Signed-off-by: Mark Cave-Ayland --- hw/isa/i82378.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 5432ab5065..63e0857208 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -34,7 +34,6 @@ struct I82378State { qemu_irq cpu_intr; qemu_irq *isa_irqs_in; - MemoryRegion io; }; static const VMStateDescription vmstate_i82378 = { From a7f4add7931ef91285fc3d89e6b3842115e09048 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 2 Jun 2023 00:30:26 +0200 Subject: [PATCH 043/412] target/s390x: Fix MXDB and MXDBR These instructions multiply 64 bits by 64 bits, not 128 bits by 64 bits. Reported-by: Tulio Magno Quites Machado Filho Fixes: 2b91240f95fd ("target/s390x: Use Int128 for passing float128") Cc: qemu-stable@nongnu.org Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2211472 Signed-off-by: Ilya Leoshkevich Message-Id: <20230601223027.795501-2-iii@linux.ibm.com> Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- target/s390x/helper.h | 2 +- target/s390x/tcg/fpu_helper.c | 5 +++-- target/s390x/tcg/insn-data.h.inc | 4 ++-- target/s390x/tcg/translate.c | 8 +------- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 7529e725f2..6bc01df73d 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -50,7 +50,7 @@ DEF_HELPER_FLAGS_3(meeb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mdeb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mdb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mxb, TCG_CALL_NO_WG, i128, env, i128, i128) -DEF_HELPER_FLAGS_3(mxdb, TCG_CALL_NO_WG, i128, env, i128, i64) +DEF_HELPER_FLAGS_3(mxdb, TCG_CALL_NO_WG, i128, env, i64, i64) DEF_HELPER_FLAGS_2(ldeb, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_3(ldxb, TCG_CALL_NO_WG, i64, env, i128, i32) DEF_HELPER_FLAGS_2(lxdb, TCG_CALL_NO_WG, i128, env, i64) diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c index 0bdab5bcf7..57e5829283 100644 --- a/target/s390x/tcg/fpu_helper.c +++ b/target/s390x/tcg/fpu_helper.c @@ -321,10 +321,11 @@ Int128 HELPER(mxb)(CPUS390XState *env, Int128 a, Int128 b) } /* 128/64-bit FP multiplication */ -Int128 HELPER(mxdb)(CPUS390XState *env, Int128 a, uint64_t f2) +Int128 HELPER(mxdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { + float128 f1_128 = float64_to_float128(f1, &env->fpu_status); float128 ret = float64_to_float128(f2, &env->fpu_status); - ret = float128_mul(ARG128(a), ret, &env->fpu_status); + ret = float128_mul(f1_128, ret, &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index 937e18ea9d..0a45dbbcda 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -668,11 +668,11 @@ F(0xb31c, MDBR, RRE, Z, f1, f2, new, f1, mdb, 0, IF_BFP) F(0xb34c, MXBR, RRE, Z, x1, x2, new_x, x1, mxb, 0, IF_BFP) F(0xb30c, MDEBR, RRE, Z, f1, e2, new, f1, mdeb, 0, IF_BFP) - F(0xb307, MXDBR, RRE, Z, 0, f2, x1, x1, mxdb, 0, IF_BFP) + F(0xb307, MXDBR, RRE, Z, f1, f2, new_x, x1, mxdb, 0, IF_BFP) F(0xed17, MEEB, RXE, Z, e1, m2_32u, new, e1, meeb, 0, IF_BFP) F(0xed1c, MDB, RXE, Z, f1, m2_64, new, f1, mdb, 0, IF_BFP) F(0xed0c, MDEB, RXE, Z, f1, m2_32u, new, f1, mdeb, 0, IF_BFP) - F(0xed07, MXDB, RXE, Z, 0, m2_64, x1, x1, mxdb, 0, IF_BFP) + F(0xed07, MXDB, RXE, Z, f1, m2_64, new_x, x1, mxdb, 0, IF_BFP) /* MULTIPLY HALFWORD */ C(0x4c00, MH, RX_a, Z, r1_o, m2_16s, new, r1_32, mul, 0) C(0xe37c, MHY, RXY_a, GIE, r1_o, m2_16s, new, r1_32, mul, 0) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 3eb3708d55..3ac573dfce 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -3421,7 +3421,7 @@ static DisasJumpType op_mxb(DisasContext *s, DisasOps *o) static DisasJumpType op_mxdb(DisasContext *s, DisasOps *o) { - gen_helper_mxdb(o->out_128, cpu_env, o->in1_128, o->in2); + gen_helper_mxdb(o->out_128, cpu_env, o->in1, o->in2); return DISAS_NEXT; } @@ -5183,12 +5183,6 @@ static void prep_r1_P(DisasContext *s, DisasOps *o) } #define SPEC_prep_r1_P SPEC_r1_even -static void prep_x1(DisasContext *s, DisasOps *o) -{ - o->out_128 = load_freg_128(get_field(s, r1)); -} -#define SPEC_prep_x1 SPEC_r1_f128 - /* ====================================================================== */ /* The "Write OUTput" generators. These generally perform some non-trivial copy of data to TCG globals, or to main memory. The trivial cases are From 2b956244a9d1b18b9653bf7453870c2d10df2427 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 2 Jun 2023 00:30:27 +0200 Subject: [PATCH 044/412] tests/tcg/s390x: Test MXDB and MXDBR Add a small test to prevent regressions. Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Message-Id: <20230601223027.795501-3-iii@linux.ibm.com> Acked-by: David Hildenbrand Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/mxdb.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/tcg/s390x/mxdb.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index a2a15c4a23..85abfbb98c 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -35,6 +35,7 @@ TESTS+=chrl TESTS+=rxsbg TESTS+=ex-relative-long TESTS+=ex-branch +TESTS+=mxdb cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread diff --git a/tests/tcg/s390x/mxdb.c b/tests/tcg/s390x/mxdb.c new file mode 100644 index 0000000000..ae922559d3 --- /dev/null +++ b/tests/tcg/s390x/mxdb.c @@ -0,0 +1,30 @@ +/* + * Test the MXDB and MXDBR instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + union { + double d[2]; + long double ld; + } a; + double b; + + a.d[0] = 1.2345; + a.d[1] = 999; + b = 6.789; + asm("mxdb %[a],%[b]" : [a] "+f" (a.ld) : [b] "R" (b)); + assert(a.ld > 8.38 && a.ld < 8.39); + + a.d[0] = 1.2345; + a.d[1] = 999; + b = 6.789; + asm("mxdbr %[a],%[b]" : [a] "+f" (a.ld) : [b] "f" (b)); + assert(a.ld > 8.38 && a.ld < 8.39); + + return EXIT_SUCCESS; +} From faae3437b054a3426ad20427e3405b7baae288bd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 24 May 2023 10:10:24 +0200 Subject: [PATCH 045/412] tests/qtest: Run ipmi-bt-test only if CONFIG_IPMI_EXTERN is set The ipmi-bt-test uses "-device ipmi-bmc-extern", thus it should only be run if this device has been enabled in the configuration. Message-Id: <20230524081024.1619273-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/qtest/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 087f2dc9d7..1d8a53a669 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -49,7 +49,8 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_SGA') ? ['boot-serial-test'] : []) + \ (config_all_devices.has_key('CONFIG_ISA_IPMI_KCS') ? ['ipmi-kcs-test'] : []) + \ (config_host.has_key('CONFIG_LINUX') and \ - config_all_devices.has_key('CONFIG_ISA_IPMI_BT') ? ['ipmi-bt-test'] : []) + \ + config_all_devices.has_key('CONFIG_ISA_IPMI_BT') and + config_all_devices.has_key('CONFIG_IPMI_EXTERN') ? ['ipmi-bt-test'] : []) + \ (config_all_devices.has_key('CONFIG_WDT_IB700') ? ['wdt_ib700-test'] : []) + \ (config_all_devices.has_key('CONFIG_PVPANIC_ISA') ? ['pvpanic-test'] : []) + \ (config_all_devices.has_key('CONFIG_PVPANIC_PCI') ? ['pvpanic-pci-test'] : []) + \ From 3d06cea8256d54a6b0238934c31012f7f17100f5 Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Tue, 11 Apr 2023 19:34:15 +0200 Subject: [PATCH 046/412] util/iov: Make qiov_slice() public We want to inline qemu_iovec_init_extended() in block/io.c for padding requests, and having access to qiov_slice() is useful for this. As a public function, it is renamed to qemu_iovec_slice(). (We will need to count the number of I/O vector elements of a slice there, and then later process this slice. Without qiov_slice(), we would need to call qemu_iovec_subvec_niov(), and all further IOV-processing functions may need to skip prefixing elements to accomodate for a qiov_offset. Because qemu_iovec_subvec_niov() internally calls qiov_slice(), we can just have the block/io.c code call qiov_slice() itself, thus get the number of elements, and also create an iovec array with the superfluous prefixing elements stripped, so the following processing functions no longer need to skip them.) Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Czenczek Message-Id: <20230411173418.19549-2-hreitz@redhat.com> --- include/qemu/iov.h | 3 +++ util/iov.c | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/qemu/iov.h b/include/qemu/iov.h index 9330746680..46fadfb27a 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -229,6 +229,9 @@ int qemu_iovec_init_extended( void *tail_buf, size_t tail_len); void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, size_t offset, size_t len); +struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, + size_t offset, size_t len, + size_t *head, size_t *tail, int *niov); int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); void qemu_iovec_concat(QEMUIOVector *dst, diff --git a/util/iov.c b/util/iov.c index b4be580022..65a70449da 100644 --- a/util/iov.c +++ b/util/iov.c @@ -378,15 +378,15 @@ static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset, } /* - * qiov_slice + * qemu_iovec_slice * * Find subarray of iovec's, containing requested range. @head would * be offset in first iov (returned by the function), @tail would be * count of extra bytes in last iovec (returned iov + @niov - 1). */ -static struct iovec *qiov_slice(QEMUIOVector *qiov, - size_t offset, size_t len, - size_t *head, size_t *tail, int *niov) +struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, + size_t offset, size_t len, + size_t *head, size_t *tail, int *niov) { struct iovec *iov, *end_iov; @@ -411,7 +411,7 @@ int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len) size_t head, tail; int niov; - qiov_slice(qiov, offset, len, &head, &tail, &niov); + qemu_iovec_slice(qiov, offset, len, &head, &tail, &niov); return niov; } @@ -439,8 +439,8 @@ int qemu_iovec_init_extended( } if (mid_len) { - mid_iov = qiov_slice(mid_qiov, mid_offset, mid_len, - &mid_head, &mid_tail, &mid_niov); + mid_iov = qemu_iovec_slice(mid_qiov, mid_offset, mid_len, + &mid_head, &mid_tail, &mid_niov); } total_niov = !!head_len + mid_niov + !!tail_len; From 18743311b829cafc1737a5f20bc3248d5f91ee2a Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Tue, 11 Apr 2023 19:34:16 +0200 Subject: [PATCH 047/412] block: Collapse padded I/O vecs exceeding IOV_MAX When processing vectored guest requests that are not aligned to the storage request alignment, we pad them by adding head and/or tail buffers for a read-modify-write cycle. The guest can submit I/O vectors up to IOV_MAX (1024) in length, but with this padding, the vector can exceed that limit. As of 4c002cef0e9abe7135d7916c51abce47f7fc1ee2 ("util/iov: make qemu_iovec_init_extended() honest"), we refuse to pad vectors beyond the limit, instead returning an error to the guest. To the guest, this appears as a random I/O error. We should not return an I/O error to the guest when it issued a perfectly valid request. Before 4c002cef0e9abe7135d7916c51abce47f7fc1ee2, we just made the vector longer than IOV_MAX, which generally seems to work (because the guest assumes a smaller alignment than we really have, file-posix's raw_co_prw() will generally see bdrv_qiov_is_aligned() return false, and so emulate the request, so that the IOV_MAX does not matter). However, that does not seem exactly great. I see two ways to fix this problem: 1. We split such long requests into two requests. 2. We join some elements of the vector into new buffers to make it shorter. I am wary of (1), because it seems like it may have unintended side effects. (2) on the other hand seems relatively simple to implement, with hopefully few side effects, so this patch does that. To do this, the use of qemu_iovec_init_extended() in bdrv_pad_request() is effectively replaced by the new function bdrv_create_padded_qiov(), which not only wraps the request IOV with padding head/tail, but also ensures that the resulting vector will not have more than IOV_MAX elements. Putting that functionality into qemu_iovec_init_extended() is infeasible because it requires allocating a bounce buffer; doing so would require many more parameters (buffer alignment, how to initialize the buffer, and out parameters like the buffer, its length, and the original elements), which is not reasonable. Conversely, it is not difficult to move qemu_iovec_init_extended()'s functionality into bdrv_create_padded_qiov() by using public qemu_iovec_* functions, so that is what this patch does. Because bdrv_pad_request() was the only "serious" user of qemu_iovec_init_extended(), the next patch will remove the latter function, so the functionality is not implemented twice. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2141964 Signed-off-by: Hanna Czenczek Message-Id: <20230411173418.19549-3-hreitz@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/io.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 151 insertions(+), 15 deletions(-) diff --git a/block/io.c b/block/io.c index f2dfc7c405..30748f0b59 100644 --- a/block/io.c +++ b/block/io.c @@ -1441,6 +1441,14 @@ out: * @merge_reads is true for small requests, * if @buf_len == @head + bytes + @tail. In this case it is possible that both * head and tail exist but @buf_len == align and @tail_buf == @buf. + * + * @write is true for write requests, false for read requests. + * + * If padding makes the vector too long (exceeding IOV_MAX), then we need to + * merge existing vector elements into a single one. @collapse_bounce_buf acts + * as the bounce buffer in such cases. @pre_collapse_qiov has the pre-collapse + * I/O vector elements so for read requests, the data can be copied back after + * the read is done. */ typedef struct BdrvRequestPadding { uint8_t *buf; @@ -1449,11 +1457,17 @@ typedef struct BdrvRequestPadding { size_t head; size_t tail; bool merge_reads; + bool write; QEMUIOVector local_qiov; + + uint8_t *collapse_bounce_buf; + size_t collapse_len; + QEMUIOVector pre_collapse_qiov; } BdrvRequestPadding; static bool bdrv_init_padding(BlockDriverState *bs, int64_t offset, int64_t bytes, + bool write, BdrvRequestPadding *pad) { int64_t align = bs->bl.request_alignment; @@ -1485,6 +1499,8 @@ static bool bdrv_init_padding(BlockDriverState *bs, pad->tail_buf = pad->buf + pad->buf_len - align; } + pad->write = write; + return true; } @@ -1549,8 +1565,23 @@ zero_mem: return 0; } -static void bdrv_padding_destroy(BdrvRequestPadding *pad) +/** + * Free *pad's associated buffers, and perform any necessary finalization steps. + */ +static void bdrv_padding_finalize(BdrvRequestPadding *pad) { + if (pad->collapse_bounce_buf) { + if (!pad->write) { + /* + * If padding required elements in the vector to be collapsed into a + * bounce buffer, copy the bounce buffer content back + */ + qemu_iovec_from_buf(&pad->pre_collapse_qiov, 0, + pad->collapse_bounce_buf, pad->collapse_len); + } + qemu_vfree(pad->collapse_bounce_buf); + qemu_iovec_destroy(&pad->pre_collapse_qiov); + } if (pad->buf) { qemu_vfree(pad->buf); qemu_iovec_destroy(&pad->local_qiov); @@ -1558,6 +1589,101 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) memset(pad, 0, sizeof(*pad)); } +/* + * Create pad->local_qiov by wrapping @iov in the padding head and tail, while + * ensuring that the resulting vector will not exceed IOV_MAX elements. + * + * To ensure this, when necessary, the first two or three elements of @iov are + * merged into pad->collapse_bounce_buf and replaced by a reference to that + * bounce buffer in pad->local_qiov. + * + * After performing a read request, the data from the bounce buffer must be + * copied back into pad->pre_collapse_qiov (e.g. by bdrv_padding_finalize()). + */ +static int bdrv_create_padded_qiov(BlockDriverState *bs, + BdrvRequestPadding *pad, + struct iovec *iov, int niov, + size_t iov_offset, size_t bytes) +{ + int padded_niov, surplus_count, collapse_count; + + /* Assert this invariant */ + assert(niov <= IOV_MAX); + + /* + * Cannot pad if resulting length would exceed SIZE_MAX. Returning an error + * to the guest is not ideal, but there is little else we can do. At least + * this will practically never happen on 64-bit systems. + */ + if (SIZE_MAX - pad->head < bytes || + SIZE_MAX - pad->head - bytes < pad->tail) + { + return -EINVAL; + } + + /* Length of the resulting IOV if we just concatenated everything */ + padded_niov = !!pad->head + niov + !!pad->tail; + + qemu_iovec_init(&pad->local_qiov, MIN(padded_niov, IOV_MAX)); + + if (pad->head) { + qemu_iovec_add(&pad->local_qiov, pad->buf, pad->head); + } + + /* + * If padded_niov > IOV_MAX, we cannot just concatenate everything. + * Instead, merge the first two or three elements of @iov to reduce the + * number of vector elements as necessary. + */ + if (padded_niov > IOV_MAX) { + /* + * Only head and tail can have lead to the number of entries exceeding + * IOV_MAX, so we can exceed it by the head and tail at most. We need + * to reduce the number of elements by `surplus_count`, so we merge that + * many elements plus one into one element. + */ + surplus_count = padded_niov - IOV_MAX; + assert(surplus_count <= !!pad->head + !!pad->tail); + collapse_count = surplus_count + 1; + + /* + * Move the elements to collapse into `pad->pre_collapse_qiov`, then + * advance `iov` (and associated variables) by those elements. + */ + qemu_iovec_init(&pad->pre_collapse_qiov, collapse_count); + qemu_iovec_concat_iov(&pad->pre_collapse_qiov, iov, + collapse_count, iov_offset, SIZE_MAX); + iov += collapse_count; + iov_offset = 0; + niov -= collapse_count; + bytes -= pad->pre_collapse_qiov.size; + + /* + * Construct the bounce buffer to match the length of the to-collapse + * vector elements, and for write requests, initialize it with the data + * from those elements. Then add it to `pad->local_qiov`. + */ + pad->collapse_len = pad->pre_collapse_qiov.size; + pad->collapse_bounce_buf = qemu_blockalign(bs, pad->collapse_len); + if (pad->write) { + qemu_iovec_to_buf(&pad->pre_collapse_qiov, 0, + pad->collapse_bounce_buf, pad->collapse_len); + } + qemu_iovec_add(&pad->local_qiov, + pad->collapse_bounce_buf, pad->collapse_len); + } + + qemu_iovec_concat_iov(&pad->local_qiov, iov, niov, iov_offset, bytes); + + if (pad->tail) { + qemu_iovec_add(&pad->local_qiov, + pad->buf + pad->buf_len - pad->tail, pad->tail); + } + + assert(pad->local_qiov.niov == MIN(padded_niov, IOV_MAX)); + return 0; +} + /* * bdrv_pad_request * @@ -1565,6 +1691,8 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) * read of padding, bdrv_padding_rmw_read() should be called separately if * needed. * + * @write is true for write requests, false for read requests. + * * Request parameters (@qiov, &qiov_offset, &offset, &bytes) are in-out: * - on function start they represent original request * - on failure or when padding is not needed they are unchanged @@ -1573,26 +1701,34 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) static int bdrv_pad_request(BlockDriverState *bs, QEMUIOVector **qiov, size_t *qiov_offset, int64_t *offset, int64_t *bytes, + bool write, BdrvRequestPadding *pad, bool *padded, BdrvRequestFlags *flags) { int ret; + struct iovec *sliced_iov; + int sliced_niov; + size_t sliced_head, sliced_tail; bdrv_check_qiov_request(*offset, *bytes, *qiov, *qiov_offset, &error_abort); - if (!bdrv_init_padding(bs, *offset, *bytes, pad)) { + if (!bdrv_init_padding(bs, *offset, *bytes, write, pad)) { if (padded) { *padded = false; } return 0; } - ret = qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head, - *qiov, *qiov_offset, *bytes, - pad->buf + pad->buf_len - pad->tail, - pad->tail); + sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes, + &sliced_head, &sliced_tail, + &sliced_niov); + + /* Guaranteed by bdrv_check_qiov_request() */ + assert(*bytes <= SIZE_MAX); + ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov, + sliced_head, *bytes); if (ret < 0) { - bdrv_padding_destroy(pad); + bdrv_padding_finalize(pad); return ret; } *bytes += pad->head + pad->tail; @@ -1659,8 +1795,8 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, flags |= BDRV_REQ_COPY_ON_READ; } - ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, - NULL, &flags); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, false, + &pad, NULL, &flags); if (ret < 0) { goto fail; } @@ -1670,7 +1806,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, bs->bl.request_alignment, qiov, qiov_offset, flags); tracked_request_end(&req); - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); fail: bdrv_dec_in_flight(bs); @@ -2002,7 +2138,7 @@ bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, /* This flag doesn't make sense for padding or zero writes */ flags &= ~BDRV_REQ_REGISTERED_BUF; - padding = bdrv_init_padding(bs, offset, bytes, &pad); + padding = bdrv_init_padding(bs, offset, bytes, true, &pad); if (padding) { assert(!(flags & BDRV_REQ_NO_WAIT)); bdrv_make_request_serialising(req, align); @@ -2050,7 +2186,7 @@ bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, } out: - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); return ret; } @@ -2118,8 +2254,8 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, * bdrv_co_do_zero_pwritev() does aligning by itself, so, we do * alignment only if there is no ZERO flag. */ - ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, - &padded, &flags); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, true, + &pad, &padded, &flags); if (ret < 0) { return ret; } @@ -2149,7 +2285,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align, qiov, qiov_offset, flags); - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); out: tracked_request_end(&req); From cc63f6f6fa1aaa4b6405dd69432c693e9c8d18ca Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Tue, 11 Apr 2023 19:34:17 +0200 Subject: [PATCH 048/412] util/iov: Remove qemu_iovec_init_extended() bdrv_pad_request() was the main user of qemu_iovec_init_extended(). HEAD^ has removed that use, so we can remove qemu_iovec_init_extended() now. The only remaining user is qemu_iovec_init_slice(), which can easily inline the small part it really needs. Note that qemu_iovec_init_extended() offered a memcpy() optimization to initialize the new I/O vector. qemu_iovec_concat_iov(), which is used to replace its functionality, does not, but calls qemu_iovec_add() for every single element. If we decide this optimization was important, we will need to re-implement it in qemu_iovec_concat_iov(), which might also benefit its pre-existing users. Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Czenczek Message-Id: <20230411173418.19549-4-hreitz@redhat.com> --- include/qemu/iov.h | 5 --- util/iov.c | 79 +++++++--------------------------------------- 2 files changed, 11 insertions(+), 73 deletions(-) diff --git a/include/qemu/iov.h b/include/qemu/iov.h index 46fadfb27a..63a1c01965 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -222,11 +222,6 @@ static inline void *qemu_iovec_buf(QEMUIOVector *qiov) void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); -int qemu_iovec_init_extended( - QEMUIOVector *qiov, - void *head_buf, size_t head_len, - QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len, - void *tail_buf, size_t tail_len); void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, size_t offset, size_t len); struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, diff --git a/util/iov.c b/util/iov.c index 65a70449da..866fb577f3 100644 --- a/util/iov.c +++ b/util/iov.c @@ -416,70 +416,6 @@ int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len) return niov; } -/* - * Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov, - * and @tail_buf buffer into new qiov. - */ -int qemu_iovec_init_extended( - QEMUIOVector *qiov, - void *head_buf, size_t head_len, - QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len, - void *tail_buf, size_t tail_len) -{ - size_t mid_head, mid_tail; - int total_niov, mid_niov = 0; - struct iovec *p, *mid_iov = NULL; - - assert(mid_qiov->niov <= IOV_MAX); - - if (SIZE_MAX - head_len < mid_len || - SIZE_MAX - head_len - mid_len < tail_len) - { - return -EINVAL; - } - - if (mid_len) { - mid_iov = qemu_iovec_slice(mid_qiov, mid_offset, mid_len, - &mid_head, &mid_tail, &mid_niov); - } - - total_niov = !!head_len + mid_niov + !!tail_len; - if (total_niov > IOV_MAX) { - return -EINVAL; - } - - if (total_niov == 1) { - qemu_iovec_init_buf(qiov, NULL, 0); - p = &qiov->local_iov; - } else { - qiov->niov = qiov->nalloc = total_niov; - qiov->size = head_len + mid_len + tail_len; - p = qiov->iov = g_new(struct iovec, qiov->niov); - } - - if (head_len) { - p->iov_base = head_buf; - p->iov_len = head_len; - p++; - } - - assert(!mid_niov == !mid_len); - if (mid_niov) { - memcpy(p, mid_iov, mid_niov * sizeof(*p)); - p[0].iov_base = (uint8_t *)p[0].iov_base + mid_head; - p[0].iov_len -= mid_head; - p[mid_niov - 1].iov_len -= mid_tail; - p += mid_niov; - } - - if (tail_len) { - p->iov_base = tail_buf; - p->iov_len = tail_len; - } - - return 0; -} - /* * Check if the contents of subrange of qiov data is all zeroes. */ @@ -511,14 +447,21 @@ bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, size_t offset, size_t len) { - int ret; + struct iovec *slice_iov; + int slice_niov; + size_t slice_head, slice_tail; assert(source->size >= len); assert(source->size - len >= offset); - /* We shrink the request, so we can't overflow neither size_t nor MAX_IOV */ - ret = qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0); - assert(ret == 0); + slice_iov = qemu_iovec_slice(source, offset, len, + &slice_head, &slice_tail, &slice_niov); + if (slice_niov == 1) { + qemu_iovec_init_buf(qiov, slice_iov[0].iov_base + slice_head, len); + } else { + qemu_iovec_init(qiov, slice_niov); + qemu_iovec_concat_iov(qiov, slice_iov, slice_niov, slice_head, len); + } } void qemu_iovec_destroy(QEMUIOVector *qiov) From d7e1905e3f54ff9512db4c7a946a8603b62b108d Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Tue, 11 Apr 2023 19:34:18 +0200 Subject: [PATCH 049/412] iotests/iov-padding: New test Test that even vectored IO requests with 1024 vector elements that are not aligned to the device's request alignment will succeed. Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Czenczek Message-Id: <20230411173418.19549-5-hreitz@redhat.com> --- tests/qemu-iotests/tests/iov-padding | 85 ++++++++++++++++++++++++ tests/qemu-iotests/tests/iov-padding.out | 59 ++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100755 tests/qemu-iotests/tests/iov-padding create mode 100644 tests/qemu-iotests/tests/iov-padding.out diff --git a/tests/qemu-iotests/tests/iov-padding b/tests/qemu-iotests/tests/iov-padding new file mode 100755 index 0000000000..b9604900c7 --- /dev/null +++ b/tests/qemu-iotests/tests/iov-padding @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Check the interaction of request padding (to fit alignment restrictions) with +# vectored I/O from the guest +# +# Copyright Red Hat +# +# 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 . +# + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file + +_make_test_img 1M + +IMGSPEC="driver=blkdebug,align=4096,image.driver=file,image.filename=$TEST_IMG" + +# Four combinations: +# - Offset 4096, length 1023 * 512 + 512: Fully aligned to 4k +# - Offset 4096, length 1023 * 512 + 4096: Head is aligned, tail is not +# - Offset 512, length 1023 * 512 + 512: Neither head nor tail are aligned +# - Offset 512, length 1023 * 512 + 4096: Tail is aligned, head is not +for start_offset in 4096 512; do + for last_element_length in 512 4096; do + length=$((1023 * 512 + $last_element_length)) + + echo + echo "== performing 1024-element vectored requests to image (offset: $start_offset; length: $length) ==" + + # Fill with data for testing + $QEMU_IO -c 'write -P 1 0 1M' "$TEST_IMG" | _filter_qemu_io + + # 1023 512-byte buffers, and then one with length $last_element_length + cmd_params="-P 2 $start_offset $(yes 512 | head -n 1023 | tr '\n' ' ') $last_element_length" + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS_NO_FMT" $QEMU_IO \ + -c "writev $cmd_params" \ + --image-opts \ + "$IMGSPEC" \ + | _filter_qemu_io + + # Read all patterns -- read the part we just wrote with writev twice, + # once "normally", and once with a readv, so we see that that works, too + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS_NO_FMT" $QEMU_IO \ + -c "read -P 1 0 $start_offset" \ + -c "read -P 2 $start_offset $length" \ + -c "readv $cmd_params" \ + -c "read -P 1 $((start_offset + length)) $((1024 * 1024 - length - start_offset))" \ + --image-opts \ + "$IMGSPEC" \ + | _filter_qemu_io + done +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/iov-padding.out b/tests/qemu-iotests/tests/iov-padding.out new file mode 100644 index 0000000000..e07a91fac7 --- /dev/null +++ b/tests/qemu-iotests/tests/iov-padding.out @@ -0,0 +1,59 @@ +QA output created by iov-padding +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 + +== performing 1024-element vectored requests to image (offset: 4096; length: 524288) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 4096 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 4096 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 4096 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 520192/520192 bytes at offset 528384 +508 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== performing 1024-element vectored requests to image (offset: 4096; length: 527872) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 527872/527872 bytes at offset 4096 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 4096 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 4096 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 516608/516608 bytes at offset 531968 +504.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== performing 1024-element vectored requests to image (offset: 512; length: 524288) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 512 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 512 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 512 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 523776/523776 bytes at offset 524800 +511.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== performing 1024-element vectored requests to image (offset: 512; length: 527872) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 527872/527872 bytes at offset 512 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 512 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 512 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 520192/520192 bytes at offset 528384 +508 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done From f5e715dbbb1ad73ea700b8179a1b8246b71447fc Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:36 +0200 Subject: [PATCH 050/412] parallels: Out of image offset in BAT leads to image inflation data_end field in BDRVParallelsState is set to the biggest offset present in BAT. If this offset is outside of the image, any further write will create the cluster at this offset and/or the image will be truncated to this offset on close. This is definitely not correct. Raise an error in parallels_open() if data_end points outside the image and it is not a check (let the check to repaire the image). Set data_end to the end of the cluster with the last correct offset. Signed-off-by: Alexander Ivanov Message-Id: <20230424093147.197643-2-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/block/parallels.c b/block/parallels.c index d8a3f13e24..7b6d770f8e 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -733,6 +733,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, BDRVParallelsState *s = bs->opaque; ParallelsHeader ph; int ret, size, i; + int64_t file_nb_sectors; QemuOpts *opts = NULL; Error *local_err = NULL; char *buf; @@ -742,6 +743,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + file_nb_sectors = bdrv_nb_sectors(bs->file->bs); + if (file_nb_sectors < 0) { + return -EINVAL; + } + ret = bdrv_pread(bs->file, 0, sizeof(ph), &ph, 0); if (ret < 0) { goto fail; @@ -806,6 +812,17 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, for (i = 0; i < s->bat_size; i++) { int64_t off = bat2sect(s, i); + if (off >= file_nb_sectors) { + if (flags & BDRV_O_CHECK) { + continue; + } + error_setg(errp, "parallels: Offset %" PRIi64 " in BAT[%d] entry " + "is larger than file size (%" PRIi64 ")", + off << BDRV_SECTOR_BITS, i, + file_nb_sectors << BDRV_SECTOR_BITS); + ret = -EINVAL; + goto fail; + } if (off >= s->data_end) { s->data_end = off + s->tracks; } From ab2d739c417ccc7c7bd6c805899c06992bf3bfb1 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:37 +0200 Subject: [PATCH 051/412] parallels: Fix high_off calculation in parallels_co_check() Don't let high_off be more than the file size even if we don't fix the image. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20230424093147.197643-3-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 7b6d770f8e..204d20685b 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -462,12 +462,12 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); res->corruptions++; if (fix & BDRV_FIX_ERRORS) { - prev_off = 0; s->bat_bitmap[i] = 0; res->corruptions_fixed++; flush_bat = true; - continue; } + prev_off = 0; + continue; } res->bfi.allocated_clusters++; From 679749ce41bc61ff92fb857a66497a03c241b8c2 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:38 +0200 Subject: [PATCH 052/412] parallels: Fix image_end_offset and data_end after out-of-image check Set data_end to the end of the last cluster inside the image. In such a way we can be sure that corrupted offsets in the BAT can't affect on the image size. If there are no allocated clusters set image_end_offset by data_end. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Message-Id: <20230424093147.197643-4-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/parallels.c b/block/parallels.c index 204d20685b..ea382e8382 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -490,7 +490,13 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, } } - res->image_end_offset = high_off + s->cluster_size; + if (high_off == 0) { + res->image_end_offset = s->data_end << BDRV_SECTOR_BITS; + } else { + res->image_end_offset = high_off + s->cluster_size; + s->data_end = res->image_end_offset >> BDRV_SECTOR_BITS; + } + if (size > res->image_end_offset) { int64_t count; count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); From b64b29b96b121614bd1c2bccc700310893b865e4 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:39 +0200 Subject: [PATCH 053/412] parallels: create parallels_set_bat_entry_helper() to assign BAT value This helper will be reused in next patches during parallels_co_check rework to simplify its code. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20230424093147.197643-5-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index ea382e8382..db6426e5f9 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -165,6 +165,13 @@ static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, return start_off; } +static void parallels_set_bat_entry(BDRVParallelsState *s, + uint32_t index, uint32_t offset) +{ + s->bat_bitmap[index] = cpu_to_le32(offset); + bitmap_set(s->bat_dirty_bmap, bat_entry_off(index) / s->bat_dirty_block, 1); +} + static int64_t coroutine_fn GRAPH_RDLOCK allocate_clusters(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) @@ -251,10 +258,8 @@ allocate_clusters(BlockDriverState *bs, int64_t sector_num, } for (i = 0; i < to_allocate; i++) { - s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier); + parallels_set_bat_entry(s, idx + i, s->data_end / s->off_multiplier); s->data_end += s->tracks; - bitmap_set(s->bat_dirty_bmap, - bat_entry_off(idx + i) / s->bat_dirty_block, 1); } return bat2sect(s, idx) + sector_num % s->tracks; From 3569cb7b789cf39273cad7c902d8f2685ebf011c Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:40 +0200 Subject: [PATCH 054/412] parallels: Use generic infrastructure for BAT writing in parallels_co_check() BAT is written in the context of conventional operations over the image inside bdrv_co_flush() when it calls parallels_co_flush_to_os() callback. Thus we should not modify BAT array directly, but call parallels_set_bat_entry() helper and bdrv_co_flush() further on. After that there is no need to manually write BAT and track its modification. This makes code more generic and allows to split parallels_set_bat_entry() for independent pieces. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Message-Id: <20230424093147.197643-6-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index db6426e5f9..fa8a56a948 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -427,9 +427,8 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, { BDRVParallelsState *s = bs->opaque; int64_t size, prev_off, high_off; - int ret; + int ret = 0; uint32_t i; - bool flush_bat = false; size = bdrv_getlength(bs->file->bs); if (size < 0) { @@ -467,9 +466,8 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); res->corruptions++; if (fix & BDRV_FIX_ERRORS) { - s->bat_bitmap[i] = 0; + parallels_set_bat_entry(s, i, 0); res->corruptions_fixed++; - flush_bat = true; } prev_off = 0; continue; @@ -486,15 +484,6 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, prev_off = off; } - ret = 0; - if (flush_bat) { - ret = bdrv_co_pwrite_sync(bs->file, 0, s->header_size, s->header, 0); - if (ret < 0) { - res->check_errors++; - goto out; - } - } - if (high_off == 0) { res->image_end_offset = s->data_end << BDRV_SECTOR_BITS; } else { @@ -529,6 +518,14 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, out: qemu_co_mutex_unlock(&s->lock); + + if (ret == 0) { + ret = bdrv_co_flush(bs); + if (ret < 0) { + res->check_errors++; + } + } + return ret; } From 96de69c7d91800345c05a60013f4187c392edfa3 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:41 +0200 Subject: [PATCH 055/412] parallels: Move check of unclean image to a separate function We will add more and more checks so we need a better code structure in parallels_co_check. Let each check performs in a separate loop in a separate helper. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20230424093147.197643-7-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index fa8a56a948..94b7408b6b 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -420,6 +420,25 @@ parallels_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, return ret; } +static void parallels_check_unclean(BlockDriverState *bs, + BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + + if (!s->header_unclean) { + return; + } + + fprintf(stderr, "%s image was not closed correctly\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); + res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + /* parallels_close will do the job right */ + res->corruptions_fixed++; + s->header_unclean = false; + } +} static int coroutine_fn GRAPH_RDLOCK parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, @@ -437,16 +456,8 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, } qemu_co_mutex_lock(&s->lock); - if (s->header_unclean) { - fprintf(stderr, "%s image was not closed correctly\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); - res->corruptions++; - if (fix & BDRV_FIX_ERRORS) { - /* parallels_close will do the job right */ - res->corruptions_fixed++; - s->header_unclean = false; - } - } + + parallels_check_unclean(bs, res, fix); res->bfi.total_clusters = s->bat_size; res->bfi.compressed_clusters = 0; /* compression is not supported */ From 6d416e56a79b91e96bdb2d2a186da647bd737d01 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:42 +0200 Subject: [PATCH 056/412] parallels: Move check of cluster outside image to a separate function We will add more and more checks so we need a better code structure in parallels_co_check. Let each check performs in a separate loop in a separate helper. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Message-Id: <20230424093147.197643-8-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 75 +++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 94b7408b6b..7f0f72e879 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -440,13 +440,55 @@ static void parallels_check_unclean(BlockDriverState *bs, } } +static int coroutine_fn GRAPH_RDLOCK +parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + uint32_t i; + int64_t off, high_off, size; + + size = bdrv_getlength(bs->file->bs); + if (size < 0) { + res->check_errors++; + return size; + } + + high_off = 0; + for (i = 0; i < s->bat_size; i++) { + off = bat2sect(s, i) << BDRV_SECTOR_BITS; + if (off > size) { + fprintf(stderr, "%s cluster %u is outside image\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); + res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + parallels_set_bat_entry(s, i, 0); + res->corruptions_fixed++; + } + continue; + } + if (high_off < off) { + high_off = off; + } + } + + if (high_off == 0) { + res->image_end_offset = s->data_end << BDRV_SECTOR_BITS; + } else { + res->image_end_offset = high_off + s->cluster_size; + s->data_end = res->image_end_offset >> BDRV_SECTOR_BITS; + } + + return 0; +} + static int coroutine_fn GRAPH_RDLOCK parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { BDRVParallelsState *s = bs->opaque; - int64_t size, prev_off, high_off; - int ret = 0; + int64_t size, prev_off; + int ret; uint32_t i; size = bdrv_getlength(bs->file->bs); @@ -459,10 +501,14 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, parallels_check_unclean(bs, res, fix); + ret = parallels_check_outside_image(bs, res, fix); + if (ret < 0) { + goto out; + } + res->bfi.total_clusters = s->bat_size; res->bfi.compressed_clusters = 0; /* compression is not supported */ - high_off = 0; prev_off = 0; for (i = 0; i < s->bat_size; i++) { int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS; @@ -471,23 +517,7 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, continue; } - /* cluster outside the image */ - if (off > size) { - fprintf(stderr, "%s cluster %u is outside image\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); - res->corruptions++; - if (fix & BDRV_FIX_ERRORS) { - parallels_set_bat_entry(s, i, 0); - res->corruptions_fixed++; - } - prev_off = 0; - continue; - } - res->bfi.allocated_clusters++; - if (off > high_off) { - high_off = off; - } if (prev_off != 0 && (prev_off + s->cluster_size) != off) { res->bfi.fragmented_clusters++; @@ -495,13 +525,6 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, prev_off = off; } - if (high_off == 0) { - res->image_end_offset = s->data_end << BDRV_SECTOR_BITS; - } else { - res->image_end_offset = high_off + s->cluster_size; - s->data_end = res->image_end_offset >> BDRV_SECTOR_BITS; - } - if (size > res->image_end_offset) { int64_t count; count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); From 9616f7a6c2eaed165e26227a4c84c24bcc2b3473 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:43 +0200 Subject: [PATCH 057/412] parallels: Fix statistics calculation Exclude out-of-image clusters from allocated and fragmented clusters calculation. Signed-off-by: Alexander Ivanov Message-Id: <20230424093147.197643-9-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block/parallels.c b/block/parallels.c index 7f0f72e879..fd1e2860d0 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -512,7 +512,11 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, prev_off = 0; for (i = 0; i < s->bat_size; i++) { int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS; - if (off == 0) { + /* + * If BDRV_FIX_ERRORS is not set, out-of-image BAT entries were not + * fixed. Skip not allocated and out-of-image BAT entries. + */ + if (off == 0 || off + s->cluster_size > res->image_end_offset) { prev_off = 0; continue; } From 09a21edfaf7f0e6145f6e9728f1a600b985ee81a Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:44 +0200 Subject: [PATCH 058/412] parallels: Move check of leaks to a separate function We will add more and more checks so we need a better code structure in parallels_co_check. Let each check performs in a separate loop in a separate helper. Signed-off-by: Alexander Ivanov Message-Id: <20230424093147.197643-10-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 74 ++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index fd1e2860d0..ba766e5e86 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -483,13 +483,12 @@ parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res, } static int coroutine_fn GRAPH_RDLOCK -parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix) +parallels_check_leak(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) { BDRVParallelsState *s = bs->opaque; - int64_t size, prev_off; + int64_t size; int ret; - uint32_t i; size = bdrv_getlength(bs->file->bs); if (size < 0) { @@ -497,6 +496,43 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, return size; } + if (size > res->image_end_offset) { + int64_t count; + count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); + fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", + fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", + size - res->image_end_offset); + res->leaks += count; + if (fix & BDRV_FIX_LEAKS) { + Error *local_err = NULL; + + /* + * In order to really repair the image, we must shrink it. + * That means we have to pass exact=true. + */ + ret = bdrv_co_truncate(bs->file, res->image_end_offset, true, + PREALLOC_MODE_OFF, 0, &local_err); + if (ret < 0) { + error_report_err(local_err); + res->check_errors++; + return ret; + } + res->leaks_fixed += count; + } + } + + return 0; +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int64_t prev_off; + int ret; + uint32_t i; + qemu_co_mutex_lock(&s->lock); parallels_check_unclean(bs, res, fix); @@ -506,6 +542,11 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, goto out; } + ret = parallels_check_leak(bs, res, fix); + if (ret < 0) { + goto out; + } + res->bfi.total_clusters = s->bat_size; res->bfi.compressed_clusters = 0; /* compression is not supported */ @@ -529,31 +570,6 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, prev_off = off; } - if (size > res->image_end_offset) { - int64_t count; - count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); - fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", - fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", - size - res->image_end_offset); - res->leaks += count; - if (fix & BDRV_FIX_LEAKS) { - Error *local_err = NULL; - - /* - * In order to really repair the image, we must shrink it. - * That means we have to pass exact=true. - */ - ret = bdrv_co_truncate(bs->file, res->image_end_offset, true, - PREALLOC_MODE_OFF, 0, &local_err); - if (ret < 0) { - error_report_err(local_err); - res->check_errors++; - goto out; - } - res->leaks_fixed += count; - } - } - out: qemu_co_mutex_unlock(&s->lock); From 7e259e2540c542b5255c3e61938b6b94f8a82a31 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:45 +0200 Subject: [PATCH 059/412] parallels: Move statistic collection to a separate function We will add more and more checks so we need a better code structure in parallels_co_check. Let each check performs in a separate loop in a separate helper. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20230424093147.197643-11-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 56 +++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index ba766e5e86..bb279ddc70 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -524,14 +524,43 @@ parallels_check_leak(BlockDriverState *bs, BdrvCheckResult *res, return 0; } +static void parallels_collect_statistics(BlockDriverState *bs, + BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int64_t off, prev_off; + uint32_t i; + + res->bfi.total_clusters = s->bat_size; + res->bfi.compressed_clusters = 0; /* compression is not supported */ + + prev_off = 0; + for (i = 0; i < s->bat_size; i++) { + off = bat2sect(s, i) << BDRV_SECTOR_BITS; + /* + * If BDRV_FIX_ERRORS is not set, out-of-image BAT entries were not + * fixed. Skip not allocated and out-of-image BAT entries. + */ + if (off == 0 || off + s->cluster_size > res->image_end_offset) { + prev_off = 0; + continue; + } + + if (prev_off != 0 && (prev_off + s->cluster_size) != off) { + res->bfi.fragmented_clusters++; + } + prev_off = off; + res->bfi.allocated_clusters++; + } +} + static int coroutine_fn GRAPH_RDLOCK parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { BDRVParallelsState *s = bs->opaque; - int64_t prev_off; int ret; - uint32_t i; qemu_co_mutex_lock(&s->lock); @@ -547,28 +576,7 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, goto out; } - res->bfi.total_clusters = s->bat_size; - res->bfi.compressed_clusters = 0; /* compression is not supported */ - - prev_off = 0; - for (i = 0; i < s->bat_size; i++) { - int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS; - /* - * If BDRV_FIX_ERRORS is not set, out-of-image BAT entries were not - * fixed. Skip not allocated and out-of-image BAT entries. - */ - if (off == 0 || off + s->cluster_size > res->image_end_offset) { - prev_off = 0; - continue; - } - - res->bfi.allocated_clusters++; - - if (prev_off != 0 && (prev_off + s->cluster_size) != off) { - res->bfi.fragmented_clusters++; - } - prev_off = off; - } + parallels_collect_statistics(bs, res, fix); out: qemu_co_mutex_unlock(&s->lock); From c0fc051dd475a3b76c9200c8bcb192710cdc1338 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:46 +0200 Subject: [PATCH 060/412] parallels: Replace qemu_co_mutex_lock by WITH_QEMU_LOCK_GUARD Replace the way we use mutex in parallels_co_check() for simplier and less error prone code. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Message-Id: <20230424093147.197643-12-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index bb279ddc70..84d1d045ad 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -562,30 +562,25 @@ parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, BDRVParallelsState *s = bs->opaque; int ret; - qemu_co_mutex_lock(&s->lock); + WITH_QEMU_LOCK_GUARD(&s->lock) { + parallels_check_unclean(bs, res, fix); - parallels_check_unclean(bs, res, fix); - - ret = parallels_check_outside_image(bs, res, fix); - if (ret < 0) { - goto out; - } - - ret = parallels_check_leak(bs, res, fix); - if (ret < 0) { - goto out; - } - - parallels_collect_statistics(bs, res, fix); - -out: - qemu_co_mutex_unlock(&s->lock); - - if (ret == 0) { - ret = bdrv_co_flush(bs); + ret = parallels_check_outside_image(bs, res, fix); if (ret < 0) { - res->check_errors++; + return ret; } + + ret = parallels_check_leak(bs, res, fix); + if (ret < 0) { + return ret; + } + + parallels_collect_statistics(bs, res, fix); + } + + ret = bdrv_co_flush(bs); + if (ret < 0) { + res->check_errors++; } return ret; From 029136f2611eaf6d1eea2d5e4bdc4492bb8ffa0c Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 24 Apr 2023 11:31:47 +0200 Subject: [PATCH 061/412] parallels: Incorrect condition in out-of-image check All the offsets in the BAT must be lower than the file size. Fix the check condition for correct check. Signed-off-by: Alexander Ivanov Reviewed-by: Denis V. Lunev Message-Id: <20230424093147.197643-13-alexander.ivanov@virtuozzo.com> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/parallels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/parallels.c b/block/parallels.c index 84d1d045ad..7c263d5085 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -457,7 +457,7 @@ parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res, high_off = 0; for (i = 0; i < s->bat_size; i++) { off = bat2sect(s, i) << BDRV_SECTOR_BITS; - if (off > size) { + if (off + s->cluster_size > size) { fprintf(stderr, "%s cluster %u is outside image\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); res->corruptions++; From 42a2890a76f4783cd1c212f27856edcf2b5e8a75 Mon Sep 17 00:00:00 2001 From: Jean-Louis Dupond Date: Mon, 5 Jun 2023 10:45:24 +0200 Subject: [PATCH 062/412] qcow2: add discard-no-unref option When we for example have a sparse qcow2 image and discard: unmap is enabled, there can be a lot of fragmentation in the image after some time. Especially on VM's that do a lot of writes/deletes. This causes the qcow2 image to grow even over 110% of its virtual size, because the free gaps in the image get too small to allocate new continuous clusters. So it allocates new space at the end of the image. Disabling discard is not an option, as discard is needed to keep the incremental backup size as low as possible. Without discard, the incremental backups would become large, as qemu thinks it's just dirty blocks but it doesn't know the blocks are unneeded. So we need to avoid fragmentation but also 'empty' the unneeded blocks in the image to have a small incremental backup. In addition, we also want to send the discards further down the stack, so the underlying blocks are still discarded. Therefor we introduce a new qcow2 option "discard-no-unref". When setting this option to true, discards will no longer have the qcow2 driver relinquish cluster allocations. Other than that, the request is handled as normal: All clusters in range are marked as zero, and, if pass-discard-request is true, it is passed further down the stack. The only difference is that the now-zero clusters are preallocated instead of being unallocated. This will avoid fragmentation on the qcow2 image. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1621 Signed-off-by: Jean-Louis Dupond Message-Id: <20230605084523.34134-2-jean-louis@dupond.be> Reviewed-by: Hanna Czenczek Signed-off-by: Hanna Czenczek --- block/qcow2-cluster.c | 32 ++++++++++++++++++++++++++++---- block/qcow2.c | 18 ++++++++++++++++++ block/qcow2.h | 3 +++ qapi/block-core.json | 12 ++++++++++++ qemu-options.hx | 12 ++++++++++++ 5 files changed, 73 insertions(+), 4 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 39cda7f907..2e76de027c 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1925,6 +1925,10 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t new_l2_bitmap = old_l2_bitmap; QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, old_l2_entry); + bool keep_reference = (cluster_type != QCOW2_CLUSTER_COMPRESSED) && + !full_discard && + (s->discard_no_unref && + type == QCOW2_DISCARD_REQUEST); /* * If full_discard is true, the cluster should not read back as zeroes, @@ -1943,10 +1947,22 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, new_l2_entry = new_l2_bitmap = 0; } else if (bs->backing || qcow2_cluster_is_allocated(cluster_type)) { if (has_subclusters(s)) { - new_l2_entry = 0; + if (keep_reference) { + new_l2_entry = old_l2_entry; + } else { + new_l2_entry = 0; + } new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; } else { - new_l2_entry = s->qcow_version >= 3 ? QCOW_OFLAG_ZERO : 0; + if (s->qcow_version >= 3) { + if (keep_reference) { + new_l2_entry |= QCOW_OFLAG_ZERO; + } else { + new_l2_entry = QCOW_OFLAG_ZERO; + } + } else { + new_l2_entry = 0; + } } } @@ -1960,8 +1976,16 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, if (has_subclusters(s)) { set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); } - /* Then decrease the refcount */ - qcow2_free_any_cluster(bs, old_l2_entry, type); + if (!keep_reference) { + /* Then decrease the refcount */ + qcow2_free_any_cluster(bs, old_l2_entry, type); + } else if (s->discard_passthrough[type] && + (cluster_type == QCOW2_CLUSTER_NORMAL || + cluster_type == QCOW2_CLUSTER_ZERO_ALLOC)) { + /* If we keep the reference, pass on the discard still */ + bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, + s->cluster_size); + } } qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); diff --git a/block/qcow2.c b/block/qcow2.c index 7f3948360d..e23edd48c2 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -682,6 +682,7 @@ static const char *const mutable_opts[] = { QCOW2_OPT_DISCARD_REQUEST, QCOW2_OPT_DISCARD_SNAPSHOT, QCOW2_OPT_DISCARD_OTHER, + QCOW2_OPT_DISCARD_NO_UNREF, QCOW2_OPT_OVERLAP, QCOW2_OPT_OVERLAP_TEMPLATE, QCOW2_OPT_OVERLAP_MAIN_HEADER, @@ -726,6 +727,11 @@ static QemuOptsList qcow2_runtime_opts = { .type = QEMU_OPT_BOOL, .help = "Generate discard requests when other clusters are freed", }, + { + .name = QCOW2_OPT_DISCARD_NO_UNREF, + .type = QEMU_OPT_BOOL, + .help = "Do not unreference discarded clusters", + }, { .name = QCOW2_OPT_OVERLAP, .type = QEMU_OPT_STRING, @@ -969,6 +975,7 @@ typedef struct Qcow2ReopenState { bool use_lazy_refcounts; int overlap_check; bool discard_passthrough[QCOW2_DISCARD_MAX]; + bool discard_no_unref; uint64_t cache_clean_interval; QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ } Qcow2ReopenState; @@ -1140,6 +1147,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, r->discard_passthrough[QCOW2_DISCARD_OTHER] = qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); + r->discard_no_unref = qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_NO_UNREF, + false); + if (r->discard_no_unref && s->qcow_version < 3) { + error_setg(errp, + "discard-no-unref is only supported since qcow2 version 3"); + ret = -EINVAL; + goto fail; + } + switch (s->crypt_method_header) { case QCOW_CRYPT_NONE: if (encryptfmt) { @@ -1220,6 +1236,8 @@ static void qcow2_update_options_commit(BlockDriverState *bs, s->discard_passthrough[i] = r->discard_passthrough[i]; } + s->discard_no_unref = r->discard_no_unref; + if (s->cache_clean_interval != r->cache_clean_interval) { cache_clean_timer_del(bs); s->cache_clean_interval = r->cache_clean_interval; diff --git a/block/qcow2.h b/block/qcow2.h index 4f67eb912a..ea9adb5706 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -133,6 +133,7 @@ #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" #define QCOW2_OPT_DISCARD_OTHER "pass-discard-other" +#define QCOW2_OPT_DISCARD_NO_UNREF "discard-no-unref" #define QCOW2_OPT_OVERLAP "overlap-check" #define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template" #define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header" @@ -385,6 +386,8 @@ typedef struct BDRVQcow2State { bool discard_passthrough[QCOW2_DISCARD_MAX]; + bool discard_no_unref; + int overlap_check; /* bitmask of Qcow2MetadataOverlap values */ bool signaled_corruption; diff --git a/qapi/block-core.json b/qapi/block-core.json index 4bf89171c6..5dd5f7e4b0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3478,6 +3478,17 @@ # @pass-discard-other: whether discard requests for the data source # should be issued on other occasions where a cluster gets freed # +# @discard-no-unref: when enabled, discards from the guest will not cause +# cluster allocations to be relinquished. This prevents qcow2 fragmentation +# that would be caused by such discards. Besides potential +# performance degradation, such fragmentation can lead to increased +# allocation of clusters past the end of the image file, +# resulting in image files whose file length can grow much larger +# than their guest disk size would suggest. +# If image file length is of concern (e.g. when storing qcow2 +# images directly on block devices), you should consider enabling +# this option. (since 8.1) +# # @overlap-check: which overlap checks to perform for writes to the # image, defaults to 'cached' (since 2.2) # @@ -3516,6 +3527,7 @@ '*pass-discard-request': 'bool', '*pass-discard-snapshot': 'bool', '*pass-discard-other': 'bool', + '*discard-no-unref': 'bool', '*overlap-check': 'Qcow2OverlapChecks', '*cache-size': 'int', '*l2-cache-size': 'int', diff --git a/qemu-options.hx b/qemu-options.hx index b37eb9662b..b57489d7ca 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1431,6 +1431,18 @@ SRST issued on other occasions where a cluster gets freed (on/off; default: off) + ``discard-no-unref`` + When enabled, discards from the guest will not cause cluster + allocations to be relinquished. This prevents qcow2 fragmentation + that would be caused by such discards. Besides potential + performance degradation, such fragmentation can lead to increased + allocation of clusters past the end of the image file, + resulting in image files whose file length can grow much larger + than their guest disk size would suggest. + If image file length is of concern (e.g. when storing qcow2 + images directly on block devices), you should consider enabling + this option. + ``overlap-check`` Which overlap checks to perform for writes to the image (none/constant/cached/all; default: cached). For details or From 98300bcdba2a60cf549c5c13761dcada4d7ff877 Mon Sep 17 00:00:00 2001 From: Camilla Conte Date: Wed, 31 May 2023 16:08:20 +0100 Subject: [PATCH 063/412] gitlab-ci: Remove unused Python package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python should have been removed in this commit: https://gitlab.com/qemu-project/qemu/-/commit/94b8b146df84ba472f461398d93fb9cdf0db8f94 Signed-off-by: Camilla Conte Message-Id: <20230531150824.32349-2-cconte@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- .gitlab-ci.d/container-template.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.d/container-template.yml b/.gitlab-ci.d/container-template.yml index 8c1370b8f0..77aa839e9e 100644 --- a/.gitlab-ci.d/container-template.yml +++ b/.gitlab-ci.d/container-template.yml @@ -7,7 +7,6 @@ before_script: - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest" - export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest" - - apk add python3 - docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" - until docker info; do sleep 1; done script: From dc96009afd8cf2372fa1bbced0bcbcbb2c5d6f1b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 30 Mar 2023 17:26:13 +0200 Subject: [PATCH 064/412] hw/mips/malta: Fix the malta machine on big endian hosts Booting a Linux kernel with the malta machine is currently broken on big endian hosts. The cpu_to_gt32 macro wants to byteswap a value for little endian targets only, but uses the wrong way to do this: cpu_to_[lb]e32 works the other way round on big endian hosts! Fix it by using the same ways on both, big and little endian hosts. Fixes: 0c8427baf0 ("hw/mips/malta: Use bootloader helper to set BAR registers") Cc: qemu-stable@nongnu.org Message-Id: <20230330152613.232082-1-thuth@redhat.com> Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth --- hw/mips/malta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index e3be2eea56..47cb49f691 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -629,9 +629,9 @@ static void bl_setup_gt64120_jump_kernel(void **p, uint64_t run_addr, /* Bus endianess is always reversed */ #if TARGET_BIG_ENDIAN -#define cpu_to_gt32 cpu_to_le32 +#define cpu_to_gt32(x) (x) #else -#define cpu_to_gt32 cpu_to_be32 +#define cpu_to_gt32(x) bswap32(x) #endif /* setup MEM-to-PCI0 mapping as done by YAMON */ From e3e2c0c82bd0b9678f7950f37f9a089301d47813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 1 Jun 2023 11:34:51 +0200 Subject: [PATCH 065/412] scripts: Add qom-cast-macro-clean-cocci-gen.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a script to generate Coccinelle semantic patch removing all pointless QOM cast macro uses. Suggested-by: Markus Armbruster Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230601093452.38972-2-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Thomas Huth --- MAINTAINERS | 1 + scripts/qom-cast-macro-clean-cocci-gen.py | 49 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 scripts/qom-cast-macro-clean-cocci-gen.py diff --git a/MAINTAINERS b/MAINTAINERS index 89f274f85e..b2137111f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3045,6 +3045,7 @@ F: include/qom/ F: qapi/qom.json F: qapi/qdev.json F: scripts/coccinelle/qom-parent-type.cocci +F: scripts/qom-cast-macro-clean-cocci-gen.py F: softmmu/qdev-monitor.c F: stubs/qdev.c F: qom/ diff --git a/scripts/qom-cast-macro-clean-cocci-gen.py b/scripts/qom-cast-macro-clean-cocci-gen.py new file mode 100644 index 0000000000..2fa8438a14 --- /dev/null +++ b/scripts/qom-cast-macro-clean-cocci-gen.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Generate a Coccinelle semantic patch to remove pointless QOM cast. +# +# Usage: +# +# $ qom-cast-macro-clean-cocci-gen.py $(git ls-files) > qom_pointless_cast.cocci +# $ spatch \ +# --macro-file scripts/cocci-macro-file.h \ +# --sp-file qom_pointless_cast.cocci \ +# --keep-comments \ +# --use-gitgrep \ +# --in-place \ +# --dir . +# +# SPDX-FileContributor: Philippe Mathieu-Daudé +# SPDX-FileCopyrightText: 2023 Linaro Ltd. +# SPDX-License-Identifier: GPL-2.0-or-later + +import re +import sys + +assert len(sys.argv) > 0 + +def print_cocci_rule(qom_typedef, qom_cast_macro): + print(f'''@@ +typedef {qom_typedef}; +{qom_typedef} *obj; +@@ +- {qom_cast_macro}(obj) ++ obj +''') + +patterns = [ + r'DECLARE_INSTANCE_CHECKER\((\w+),\W*(\w+),\W*TYPE_\w+\)', + r'DECLARE_OBJ_CHECKERS\((\w+),\W*\w+,\W*(\w+),\W*TYPE_\w+\)', + r'OBJECT_DECLARE_TYPE\((\w+),\W*\w+,\W*(\w+)\)', + r'OBJECT_DECLARE_SIMPLE_TYPE\((\w+),\W*(\w+)\)', + r'INTERFACE_CHECK\((\w+),\W*\(\w+\),\W*TYPE_(\w+)\)', +] + +for fn in sys.argv[1:]: + try: + content = open(fn, 'rt').read() + except: + continue + for pattern in patterns: + for match in re.findall(pattern, content): + print_cocci_rule(match[0], match[1]) From 7d5b0d68647c8f966c6e4e68ed669127186b4701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 1 Jun 2023 11:34:52 +0200 Subject: [PATCH 066/412] bulk: Remove pointless QOM casts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change running Coccinelle spatch with content generated from the qom-cast-macro-clean-cocci-gen.py added in the previous commit. Suggested-by: Markus Armbruster Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230601093452.38972-3-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Thomas Huth --- block/nbd.c | 4 ++-- chardev/char-pty.c | 2 +- hw/arm/musicpal.c | 2 +- hw/arm/xlnx-versal.c | 2 +- hw/display/vhost-user-gpu.c | 4 ++-- hw/intc/loongarch_extioi.c | 6 +++--- hw/m68k/q800.c | 2 +- hw/pci-host/bonito.c | 2 +- hw/ppc/pnv_lpc.c | 2 +- hw/ppc/pnv_occ.c | 2 +- hw/ppc/pnv_sbe.c | 2 +- hw/riscv/virt.c | 10 +++++----- hw/rx/rx62n.c | 2 +- hw/scsi/esp-pci.c | 18 +++++++++--------- hw/sparc/sun4m.c | 4 ++-- hw/virtio/virtio-mem-pci.c | 6 +++--- hw/virtio/virtio-pmem-pci.c | 6 +++--- migration/fd.c | 4 ++-- migration/multifd.c | 2 +- migration/yank_functions.c | 4 ++-- nbd/client-connection.c | 2 +- nbd/server.c | 2 +- softmmu/qdev-monitor.c | 2 +- ui/vnc-ws.c | 6 +++--- 24 files changed, 49 insertions(+), 49 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index a3f8f8a9d5..5aef5cb6bd 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -397,7 +397,7 @@ static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) /* Finalize previous connection if any */ if (s->ioc) { - qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + qio_channel_detach_aio_context(s->ioc); yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank, s->bs); object_unref(OBJECT(s->ioc)); @@ -1455,7 +1455,7 @@ static void nbd_yank(void *opaque) BDRVNBDState *s = (BDRVNBDState *)bs->opaque; QEMU_LOCK_GUARD(&s->requests_lock); - qio_channel_shutdown(QIO_CHANNEL(s->ioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); s->state = NBD_CLIENT_QUIT; } diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 92fd33c854..4e5deac18a 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -334,7 +334,7 @@ static void char_pty_open(Chardev *chr, s = PTY_CHARDEV(chr); s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); name = g_strdup_printf("chardev-pty-%s", chr->label); - qio_channel_set_name(QIO_CHANNEL(s->ioc), name); + qio_channel_set_name(s->ioc, name); g_free(name); s->timer_src = NULL; *be_opened = false; diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 58f3d30c9b..dc4e43e0ee 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1250,7 +1250,7 @@ static void musicpal_init(MachineState *machine) uart_orgate = DEVICE(object_new(TYPE_OR_IRQ)); object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(uart_orgate, NULL, &error_fatal); - qdev_connect_gpio_out(DEVICE(uart_orgate), 0, + qdev_connect_gpio_out(uart_orgate, 0, qdev_get_gpio_in(pic, MP_UART_SHARED_IRQ)); serial_mm_init(address_space_mem, MP_UART1_BASE, 2, diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 69b1b99e93..db1e0dee6e 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -327,7 +327,7 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) object_initialize_child(OBJECT(s), "rtc", &s->pmc.rtc, TYPE_XLNX_ZYNQMP_RTC); sbd = SYS_BUS_DEVICE(&s->pmc.rtc); - sysbus_realize(SYS_BUS_DEVICE(sbd), &error_fatal); + sysbus_realize(sbd, &error_fatal); mr = sysbus_mmio_get_region(sbd, 0); memory_region_add_subregion(&s->mr_ps, MM_PMC_RTC, mr); diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 71dfd956b8..1386e869e5 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -364,11 +364,11 @@ vhost_user_gpu_gl_flushed(VirtIOGPUBase *b) VhostUserGPU *g = VHOST_USER_GPU(b); if (g->backend_blocked) { - vhost_user_gpu_unblock(VHOST_USER_GPU(g)); + vhost_user_gpu_unblock(g); g->backend_blocked = false; } - vhost_user_gpu_update_blocked(VHOST_USER_GPU(g), false); + vhost_user_gpu_update_blocked(g, false); } static bool diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 0e7a3e32f3..af75460643 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -276,7 +276,7 @@ static void loongarch_extioi_instance_init(Object *obj) int i, cpu, pin; for (i = 0; i < EXTIOI_IRQS; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); + sysbus_init_irq(dev, &s->irq[i]); } qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS); @@ -284,14 +284,14 @@ static void loongarch_extioi_instance_init(Object *obj) for (cpu = 0; cpu < EXTIOI_CPUS; cpu++) { memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops, s, "extioi_iocsr", 0x900); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_iocsr_mem[cpu]); + sysbus_init_mmio(dev, &s->extioi_iocsr_mem[cpu]); for (pin = 0; pin < LS3A_INTC_IP; pin++) { qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1); } } memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, s, "extioi_system_mem", 0x900); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_system_mem); + sysbus_init_mmio(dev, &s->extioi_system_mem); } static void loongarch_extioi_class_init(ObjectClass *klass, void *data) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index b35ecafbc7..68f0cd8cac 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -525,7 +525,7 @@ static void q800_init(MachineState *machine) qdev_realize_and_unref(escc_orgate, NULL, &error_fatal); sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(escc_orgate, 0)); sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(escc_orgate, 1)); - qdev_connect_gpio_out(DEVICE(escc_orgate), 0, + qdev_connect_gpio_out(escc_orgate, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_ESCC)); sysbus_mmio_map(sysbus, 0, SCC_BASE); diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 1cf25bab8d..4701481b9b 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -656,7 +656,7 @@ static void bonito_pci_realize(PCIDevice *dev, Error **errp) PCIBonitoState *s = PCI_BONITO(dev); SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - BonitoState *bs = BONITO_PCI_HOST_BRIDGE(s->pcihost); + BonitoState *bs = s->pcihost; MemoryRegion *pcimem_alias = g_new(MemoryRegion, 1); /* diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 605d390861..d692858bee 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -744,7 +744,7 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); - qdev_init_gpio_out(DEVICE(dev), &lpc->psi_irq, 1); + qdev_init_gpio_out(dev, &lpc->psi_irq, 1); } static void pnv_lpc_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 9fa6d91d31..48123ceae1 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -278,7 +278,7 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) occ, "occ-common-area", PNV_OCC_SENSOR_DATA_BLOCK_SIZE); - qdev_init_gpio_out(DEVICE(dev), &occ->psi_irq, 1); + qdev_init_gpio_out(dev, &occ->psi_irq, 1); } static void pnv_occ_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_sbe.c b/hw/ppc/pnv_sbe.c index 1c7812a135..74cee4eea7 100644 --- a/hw/ppc/pnv_sbe.c +++ b/hw/ppc/pnv_sbe.c @@ -381,7 +381,7 @@ static void pnv_sbe_realize(DeviceState *dev, Error **errp) psc->xscom_mbox_ops, sbe, "xscom-sbe-mbox", psc->xscom_mbox_size); - qdev_init_gpio_out(DEVICE(dev), &sbe->psi_irq, 1); + qdev_init_gpio_out(dev, &sbe->psi_irq, 1); sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe); } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4e3efbee16..245c7b97b2 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1488,7 +1488,7 @@ static void virt_machine_init(MachineState *machine) for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, - qdev_get_gpio_in(DEVICE(virtio_irqchip), VIRTIO_IRQ + i)); + qdev_get_gpio_in(virtio_irqchip, VIRTIO_IRQ + i)); } gpex_pcie_init(system_memory, @@ -1499,16 +1499,16 @@ static void virt_machine_init(MachineState *machine) virt_high_pcie_memmap.base, virt_high_pcie_memmap.size, memmap[VIRT_PCIE_PIO].base, - DEVICE(pcie_irqchip)); + pcie_irqchip); - create_platform_bus(s, DEVICE(mmio_irqchip)); + create_platform_bus(s, mmio_irqchip); serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, qdev_get_gpio_in(DEVICE(mmio_irqchip), UART0_IRQ), 399193, + 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, - qdev_get_gpio_in(DEVICE(mmio_irqchip), RTC_IRQ)); + qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); virt_flash_create(s); diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index fa5add9f9d..3e887a0fc7 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -154,7 +154,7 @@ static void register_icu(RX62NState *s) sysbus_connect_irq(icu, 0, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_IRQ)); sysbus_connect_irq(icu, 1, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_FIR)); sysbus_connect_irq(icu, 2, s->irq[SWI]); - sysbus_mmio_map(SYS_BUS_DEVICE(icu), 0, RX62N_ICU_BASE); + sysbus_mmio_map(icu, 0, RX62N_ICU_BASE); } static void register_tmr(RX62NState *s, int unit) diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 2f7f11e70b..4e890db0e2 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -79,7 +79,7 @@ struct PCIESPState { static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; trace_esp_pci_dma_idle(val); esp_dma_enable(s, 0, 0); @@ -93,7 +93,7 @@ static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; trace_esp_pci_dma_abort(val); if (s->current_req) { @@ -103,7 +103,7 @@ static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) static void esp_pci_handle_start(PCIESPState *pci, uint32_t val) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; trace_esp_pci_dma_start(val); @@ -161,7 +161,7 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; uint32_t val; val = pci->dma_regs[saddr]; @@ -183,7 +183,7 @@ static void esp_pci_io_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { PCIESPState *pci = opaque; - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; if (size < 4 || addr & 3) { /* need to upgrade request: we only support 4-bytes accesses */ @@ -228,7 +228,7 @@ static uint64_t esp_pci_io_read(void *opaque, hwaddr addr, unsigned int size) { PCIESPState *pci = opaque; - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; uint32_t ret; if (addr < 0x40) { @@ -315,7 +315,7 @@ static const MemoryRegionOps esp_pci_io_ops = { static void esp_pci_hard_reset(DeviceState *dev) { PCIESPState *pci = PCI_ESP(dev); - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; esp_hard_reset(s); pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P @@ -366,7 +366,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) { PCIESPState *pci = PCI_ESP(dev); DeviceState *d = DEVICE(dev); - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; uint8_t *pci_conf; if (!qdev_realize(DEVICE(s), NULL, errp)) { @@ -394,7 +394,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) static void esp_pci_scsi_exit(PCIDevice *d) { PCIESPState *pci = PCI_ESP(d); - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; qemu_free_irq(s->irq); } diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index d9288326d6..17bf5f2879 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -982,7 +982,7 @@ static void sun4m_hw_init(MachineState *machine) qdev_realize_and_unref(ms_kb_orgate, NULL, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(ms_kb_orgate, 0)); sysbus_connect_irq(s, 1, qdev_get_gpio_in(ms_kb_orgate, 1)); - qdev_connect_gpio_out(DEVICE(ms_kb_orgate), 0, slavio_irq[14]); + qdev_connect_gpio_out(ms_kb_orgate, 0, slavio_irq[14]); dev = qdev_new(TYPE_ESCC); qdev_prop_set_uint32(dev, "disabled", 0); @@ -1004,7 +1004,7 @@ static void sun4m_hw_init(MachineState *machine) qdev_realize_and_unref(serial_orgate, NULL, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(serial_orgate, 0)); sysbus_connect_irq(s, 1, qdev_get_gpio_in(serial_orgate, 1)); - qdev_connect_gpio_out(DEVICE(serial_orgate), 0, slavio_irq[15]); + qdev_connect_gpio_out(serial_orgate, 0, slavio_irq[15]); if (hwdef->apc_base) { apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0)); diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index e8c338c5d9..b85c12668d 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -42,7 +42,7 @@ static MemoryRegion *virtio_mem_pci_get_memory_region(MemoryDeviceState *md, Error **errp) { VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); - VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEM *vmem = &pci_mem->vdev; VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); return vmc->get_memory_region(vmem, errp); @@ -60,7 +60,7 @@ static void virtio_mem_pci_fill_device_info(const MemoryDeviceState *md, { VirtioMEMDeviceInfo *vi = g_new0(VirtioMEMDeviceInfo, 1); VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); - VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEM *vmem = &pci_mem->vdev; VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); DeviceState *dev = DEVICE(md); @@ -123,7 +123,7 @@ static void virtio_mem_pci_instance_init(Object *obj) TYPE_VIRTIO_MEM); dev->size_change_notifier.notify = virtio_mem_pci_size_change_notify; - vmem = VIRTIO_MEM(&dev->vdev); + vmem = &dev->vdev; vmc = VIRTIO_MEM_GET_CLASS(vmem); /* * We never remove the notifier again, as we expect both devices to diff --git a/hw/virtio/virtio-pmem-pci.c b/hw/virtio/virtio-pmem-pci.c index 1b89ade9d1..197d219204 100644 --- a/hw/virtio/virtio-pmem-pci.c +++ b/hw/virtio/virtio-pmem-pci.c @@ -42,7 +42,7 @@ static MemoryRegion *virtio_pmem_pci_get_memory_region(MemoryDeviceState *md, Error **errp) { VirtIOPMEMPCI *pci_pmem = VIRTIO_PMEM_PCI(md); - VirtIOPMEM *pmem = VIRTIO_PMEM(&pci_pmem->vdev); + VirtIOPMEM *pmem = &pci_pmem->vdev; VirtIOPMEMClass *vpc = VIRTIO_PMEM_GET_CLASS(pmem); return vpc->get_memory_region(pmem, errp); @@ -52,7 +52,7 @@ static uint64_t virtio_pmem_pci_get_plugged_size(const MemoryDeviceState *md, Error **errp) { VirtIOPMEMPCI *pci_pmem = VIRTIO_PMEM_PCI(md); - VirtIOPMEM *pmem = VIRTIO_PMEM(&pci_pmem->vdev); + VirtIOPMEM *pmem = &pci_pmem->vdev; VirtIOPMEMClass *vpc = VIRTIO_PMEM_GET_CLASS(pmem); MemoryRegion *mr = vpc->get_memory_region(pmem, errp); @@ -65,7 +65,7 @@ static void virtio_pmem_pci_fill_device_info(const MemoryDeviceState *md, { VirtioPMEMDeviceInfo *vi = g_new0(VirtioPMEMDeviceInfo, 1); VirtIOPMEMPCI *pci_pmem = VIRTIO_PMEM_PCI(md); - VirtIOPMEM *pmem = VIRTIO_PMEM(&pci_pmem->vdev); + VirtIOPMEM *pmem = &pci_pmem->vdev; VirtIOPMEMClass *vpc = VIRTIO_PMEM_GET_CLASS(pmem); DeviceState *dev = DEVICE(md); diff --git a/migration/fd.c b/migration/fd.c index 6f2f50475f..0eb677dcae 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -38,7 +38,7 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** return; } - qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-outgoing"); + qio_channel_set_name(ioc, "migration-fd-outgoing"); migration_channel_connect(s, ioc, NULL, NULL); object_unref(OBJECT(ioc)); } @@ -68,7 +68,7 @@ void fd_start_incoming_migration(const char *fdname, Error **errp) return; } - qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-incoming"); + qio_channel_set_name(ioc, "migration-fd-incoming"); qio_channel_add_watch_full(ioc, G_IO_IN, fd_accept_incoming_migration, NULL, NULL, diff --git a/migration/multifd.c b/migration/multifd.c index 0bf5958a9c..3387d8277f 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -894,7 +894,7 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) trace_multifd_new_send_channel_async(p->id); if (!qio_task_propagate_error(task, &local_err)) { - p->c = QIO_CHANNEL(sioc); + p->c = sioc; qio_channel_set_delay(p->c, false); p->running = true; if (multifd_channel_connect(p, sioc, local_err)) { diff --git a/migration/yank_functions.c b/migration/yank_functions.c index 8c08aef14a..d5a710a3f2 100644 --- a/migration/yank_functions.c +++ b/migration/yank_functions.c @@ -35,7 +35,7 @@ void migration_ioc_register_yank(QIOChannel *ioc) if (migration_ioc_yank_supported(ioc)) { yank_register_function(MIGRATION_YANK_INSTANCE, migration_yank_iochannel, - QIO_CHANNEL(ioc)); + ioc); } } @@ -44,7 +44,7 @@ void migration_ioc_unregister_yank(QIOChannel *ioc) if (migration_ioc_yank_supported(ioc)) { yank_unregister_function(MIGRATION_YANK_INSTANCE, migration_yank_iochannel, - QIO_CHANNEL(ioc)); + ioc); } } diff --git a/nbd/client-connection.c b/nbd/client-connection.c index e5b1046a1c..3d14296c04 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -156,7 +156,7 @@ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, * channel. */ if (outioc && *outioc) { - qio_channel_close(QIO_CHANNEL(*outioc), NULL); + qio_channel_close(*outioc, NULL); object_unref(OBJECT(*outioc)); *outioc = NULL; } else { diff --git a/nbd/server.c b/nbd/server.c index 2664d43bff..febe001a39 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1189,7 +1189,7 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) } ret = 0; object_unref(OBJECT(client->ioc)); - client->ioc = QIO_CHANNEL(tioc); + client->ioc = tioc; break; case NBD_OPT_EXPORT_NAME: diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index b8d2c4dadd..74f4e41338 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -711,7 +711,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, goto err_del_dev; } - if (!qdev_realize(DEVICE(dev), bus, errp)) { + if (!qdev_realize(dev, bus, errp)) { goto err_del_dev; } return dev; diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 6d79f3e5a5..9e3503d93d 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -40,9 +40,9 @@ static void vncws_tls_handshake_done(QIOTask *task, if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); } - vs->ioc_tag = qio_channel_add_watch( - QIO_CHANNEL(vs->ioc), G_IO_IN | G_IO_HUP | G_IO_ERR, - vncws_handshake_io, vs, NULL); + vs->ioc_tag = qio_channel_add_watch(vs->ioc, + G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_handshake_io, vs, NULL); } } From 71b11cbe1c34411238703abe24bfaf2e9712c30d Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 5 Jun 2023 13:39:47 +0200 Subject: [PATCH 067/412] s390x/tcg: Fix CPU address returned by STIDP In qemu-user-s390x, /proc/cpuinfo contains: processor 0: version = 00, identification = 000000, machine = 8561 processor 1: version = 00, identification = 400000, machine = 8561 The highest nibble is supposed to contain the CPU address, but it's off by 2 bits. Fix the shift value and provide a symbolic constant for it. With the fix we get: processor 0: version = 00, identification = 000000, machine = 8561 processor 1: version = 00, identification = 100000, machine = 8561 Fixes: 076d4d39b65f ("s390x/cpumodel: wire up cpu type + id for TCG") Reviewed-by: David Hildenbrand Signed-off-by: Ilya Leoshkevich Message-Id: <20230605113950.1169228-2-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 4 ++-- target/s390x/cpu_models.h | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 457b5cb10c..ae8880e81d 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -607,8 +607,8 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) #if !defined(CONFIG_USER_ONLY) cpu->env.cpuid = s390_cpuid_from_cpu_model(cpu->model); if (tcg_enabled()) { - /* basic mode, write the cpu address into the first 4 bit of the ID */ - cpu->env.cpuid = deposit64(cpu->env.cpuid, 54, 4, cpu->env.core_id); + cpu->env.cpuid = deposit64(cpu->env.cpuid, CPU_PHYS_ADDR_SHIFT, + CPU_PHYS_ADDR_BITS, cpu->env.core_id); } #endif } diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index fb1adc8b21..cc7305ec21 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -96,10 +96,18 @@ static inline bool s390_known_cpu_type(uint16_t type) { return s390_get_gen_for_cpu_type(type) != 0; } +#define CPU_ID_SHIFT 32 +#define CPU_ID_BITS 24 +/* + * When cpu_id_format is 0 (basic mode), the leftmost 4 bits of cpu_id contain + * the rightmost 4 bits of the physical CPU address. + */ +#define CPU_PHYS_ADDR_BITS 4 +#define CPU_PHYS_ADDR_SHIFT (CPU_ID_SHIFT + CPU_ID_BITS - CPU_PHYS_ADDR_BITS) static inline uint64_t s390_cpuid_from_cpu_model(const S390CPUModel *model) { return ((uint64_t)model->cpu_ver << 56) | - ((uint64_t)model->cpu_id << 32) | + ((uint64_t)model->cpu_id << CPU_ID_SHIFT) | ((uint64_t)model->def->type << 16) | (model->def->gen == 7 ? 0 : (uint64_t)model->cpu_id_format << 15); } From e1b819c804314c87555e143fd9ea2858c9d2d34c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 5 Jun 2023 13:39:48 +0200 Subject: [PATCH 068/412] linux-user/elfload: Expose get_elf_hwcap() on s390x It is required for implementing /proc/cpuinfo emulation. Reviewed-by: David Hildenbrand Signed-off-by: Ilya Leoshkevich Message-Id: <20230605113950.1169228-3-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- linux-user/elfload.c | 2 +- linux-user/loader.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 418ad92598..49ec9ccc94 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1583,7 +1583,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define GET_FEATURE(_feat, _hwcap) \ do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) -static uint32_t get_elf_hwcap(void) +uint32_t get_elf_hwcap(void) { /* * Let's assume we always have esan3 and zarch. diff --git a/linux-user/loader.h b/linux-user/loader.h index f375ee0679..ad6ca9dbe3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -56,4 +56,8 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src, extern unsigned long guest_stack_size; +#ifdef TARGET_S390X +uint32_t get_elf_hwcap(void); +#endif + #endif /* LINUX_USER_LOADER_H */ From e19807bee357a34b1c56f527203cf6be06fe663b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 5 Jun 2023 13:39:49 +0200 Subject: [PATCH 069/412] linux-user/elfload: Introduce elf_hwcap_str() on s390x It is required for implementing /proc/cpuinfo emulation. Reviewed-by: David Hildenbrand Signed-off-by: Ilya Leoshkevich Message-Id: <20230605113950.1169228-4-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- linux-user/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/loader.h | 1 + 2 files changed, 28 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 49ec9ccc94..d80d68484b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1605,6 +1605,33 @@ uint32_t get_elf_hwcap(void) return hwcap; } +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [HWCAP_S390_ESAN3] = "esan3", + [HWCAP_S390_ZARCH] = "zarch", + [HWCAP_S390_STFLE] = "stfle", + [HWCAP_S390_MSA] = "msa", + [HWCAP_S390_LDISP] = "ldisp", + [HWCAP_S390_EIMM] = "eimm", + [HWCAP_S390_DFP] = "dfp", + [HWCAP_S390_HPAGE] = "edat", + [HWCAP_S390_ETF3EH] = "etf3eh", + [HWCAP_S390_HIGH_GPRS] = "highgprs", + [HWCAP_S390_TE] = "te", + [HWCAP_S390_VXRS] = "vx", + [HWCAP_S390_VXRS_BCD] = "vxd", + [HWCAP_S390_VXRS_EXT] = "vxe", + [HWCAP_S390_GS] = "gs", + [HWCAP_S390_VXRS_EXT2] = "vxe2", + [HWCAP_S390_VXRS_PDE] = "vxp", + [HWCAP_S390_SORT] = "sort", + [HWCAP_S390_DFLT] = "dflt", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->psw.addr = infop->entry; diff --git a/linux-user/loader.h b/linux-user/loader.h index ad6ca9dbe3..59cbeacf24 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -58,6 +58,7 @@ extern unsigned long guest_stack_size; #ifdef TARGET_S390X uint32_t get_elf_hwcap(void); +const char *elf_hwcap_str(uint32_t bit); #endif #endif /* LINUX_USER_LOADER_H */ From 1fb9bdaf59719c0d0c28043e58c8e3452fd6d7de Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 5 Jun 2023 13:39:50 +0200 Subject: [PATCH 070/412] linux-user: Emulate /proc/cpuinfo on s390x Some s390x userspace programs are confused when seeing a foreign /proc/cpuinfo [1]. Add the emulation for s390x; follow the respective kernel code structure where possible. Output example: vendor_id : IBM/S390 # processors : 12 bogomips per cpu: 13370.00 max thread id : 0 features : esan3 zarch stfle msa facilities : 0 1 2 3 4 7 9 16 17 18 19 21 22 24 25 27 30 31 32 33 34 35 37 40 41 45 49 51 52 53 57 58 61 69 71 72 75 76 77 129 130 131 135 138 146 148 processor 0: version = 00, identification = 000000, machine = 8561 processor 1: version = 00, identification = 100000, machine = 8561 [...] cpu number : 0 version : 00 identification : 000000 machine : 8561 cpu number : 1 version : 00 identification : 100000 machine : 8561 [...] [1] https://bugzilla.redhat.com/show_bug.cgi?id=2211472 Reported-by: Tulio Magno Quites Machado Filho Reviewed-by: David Hildenbrand Signed-off-by: Ilya Leoshkevich Message-Id: <20230605113950.1169228-5-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- linux-user/syscall.c | 106 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 89b58b386b..83685f0aa5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8232,7 +8232,7 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code) #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \ defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) || \ - defined(TARGET_RISCV) + defined(TARGET_RISCV) || defined(TARGET_S390X) static int is_proc(const char *filename, const char *entry) { return strcmp(filename, entry) == 0; @@ -8339,6 +8339,107 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) } #endif +#if defined(TARGET_S390X) +/* + * Emulate what a Linux kernel running in qemu-system-s390x -M accel=tcg would + * show in /proc/cpuinfo. + * + * Skip the following in order to match the missing support in op_ecag(): + * - show_cacheinfo(). + * - show_cpu_topology(). + * - show_cpu_mhz(). + * + * Use fixed values for certain fields: + * - bogomips per cpu - from a qemu-system-s390x run. + * - max thread id = 0, since SMT / SIGP_SET_MULTI_THREADING is not supported. + * + * Keep the code structure close to arch/s390/kernel/processor.c. + */ + +static void show_facilities(int fd) +{ + size_t sizeof_stfl_bytes = 2048; + g_autofree uint8_t *stfl_bytes = g_new0(uint8_t, sizeof_stfl_bytes); + unsigned int bit; + + dprintf(fd, "facilities :"); + s390_get_feat_block(S390_FEAT_TYPE_STFL, stfl_bytes); + for (bit = 0; bit < sizeof_stfl_bytes * 8; bit++) { + if (test_be_bit(bit, stfl_bytes)) { + dprintf(fd, " %d", bit); + } + } + dprintf(fd, "\n"); +} + +static int cpu_ident(unsigned long n) +{ + return deposit32(0, CPU_ID_BITS - CPU_PHYS_ADDR_BITS, CPU_PHYS_ADDR_BITS, + n); +} + +static void show_cpu_summary(CPUArchState *cpu_env, int fd) +{ + S390CPUModel *model = env_archcpu(cpu_env)->model; + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + uint32_t elf_hwcap = get_elf_hwcap(); + const char *hwcap_str; + int i; + + dprintf(fd, "vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: 13370.00\n", + num_cpus); + dprintf(fd, "max thread id : 0\n"); + dprintf(fd, "features\t: "); + for (i = 0; i < sizeof(elf_hwcap) * 8; i++) { + if (!(elf_hwcap & (1 << i))) { + continue; + } + hwcap_str = elf_hwcap_str(i); + if (hwcap_str) { + dprintf(fd, "%s ", hwcap_str); + } + } + dprintf(fd, "\n"); + show_facilities(fd); + for (i = 0; i < num_cpus; i++) { + dprintf(fd, "processor %d: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + i, model->cpu_ver, cpu_ident(i), model->def->type); + } +} + +static void show_cpu_ids(CPUArchState *cpu_env, int fd, unsigned long n) +{ + S390CPUModel *model = env_archcpu(cpu_env)->model; + + dprintf(fd, "version : %02X\n", model->cpu_ver); + dprintf(fd, "identification : %06X\n", cpu_ident(n)); + dprintf(fd, "machine : %04X\n", model->def->type); +} + +static void show_cpuinfo(CPUArchState *cpu_env, int fd, unsigned long n) +{ + dprintf(fd, "\ncpu number : %ld\n", n); + show_cpu_ids(cpu_env, fd, n); +} + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + int i; + + show_cpu_summary(cpu_env, fd); + for (i = 0; i < num_cpus; i++) { + show_cpuinfo(cpu_env, fd, i); + } + return 0; +} +#endif + #if defined(TARGET_M68K) static int open_hardware(CPUArchState *cpu_env, int fd) { @@ -8363,7 +8464,8 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN { "/proc/net/route", open_net_route, is_proc }, #endif -#if defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined(TARGET_RISCV) +#if defined(TARGET_SPARC) || defined(TARGET_HPPA) || \ + defined(TARGET_RISCV) || defined(TARGET_S390X) { "/proc/cpuinfo", open_cpuinfo, is_proc }, #endif #if defined(TARGET_M68K) From 79ee1b4d44667d358d73844c9e668f01f3d644ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Apr 2023 15:45:50 +0100 Subject: [PATCH 071/412] tcg/ppc: Remove TARGET_LONG_BITS, TCG_TYPE_TL All uses replaced with TCGContext.addr_type. Reviewed-by: Anton Johansson Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target.c.inc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index d47a9e3478..11955a6cc2 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2046,6 +2046,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, TCGReg addrlo, TCGReg addrhi, MemOpIdx oi, bool is_ld) { + TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); MemOp a_bits, s_bits; @@ -2098,17 +2099,18 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); /* Load the (low part) TLB comparator into TMP2. */ - if (cmp_off == 0 && TCG_TARGET_REG_BITS >= TARGET_LONG_BITS) { - uint32_t lxu = (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32 + if (cmp_off == 0 + && (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32)) { + uint32_t lxu = (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32 ? LWZUX : LDUX); tcg_out32(s, lxu | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); } else { tcg_out32(s, ADD | TAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP2)); - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off + 4 * HOST_BIG_ENDIAN); } else { - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); + tcg_out_ld(s, addr_type, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); } } @@ -2116,7 +2118,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * Load the TLB addend for use on the fast path. * Do this asap to minimize any load use delay. */ - if (TCG_TARGET_REG_BITS >= TARGET_LONG_BITS) { + if (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32) { tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, offsetof(CPUTLBEntry, addend)); } @@ -2151,7 +2153,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } /* Mask the address for the requested alignment. */ - if (TARGET_LONG_BITS == 32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, (32 - a_bits) & 31, 31 - s->page_bits); } else if (a_bits == 0) { @@ -2163,7 +2165,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } } - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { /* Low part comparison into cr7. */ tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, 0, 7, TCG_TYPE_I32); @@ -2183,8 +2185,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); } else { /* Full comparison into cr7. */ - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, - 0, 7, TCG_TYPE_TL); + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, 0, 7, addr_type); } /* Load a pointer into the current opcode w/conditional branch-link. */ @@ -2211,7 +2212,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, h->base = guest_base ? TCG_GUEST_BASE_REG : 0; #endif - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { /* Zero-extend the guest address for use in the host address. */ tcg_out_ext32u(s, TCG_REG_R0, addrlo); h->index = TCG_REG_R0; From 8aefe1fb8afb4acd7b2ece8a3b0b17bdfdc23604 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Apr 2023 15:59:31 +0100 Subject: [PATCH 072/412] tcg/riscv: Remove TARGET_LONG_BITS, TCG_TYPE_TL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All uses replaced with TCGContext.addr_type. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target.c.inc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index c0257124fa..a8f99f7e77 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1195,6 +1195,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { + TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); TCGAtomAlign aa; @@ -1236,19 +1237,19 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, addr_adj = addr_reg; if (a_mask < s_mask) { addr_adj = TCG_REG_TMP0; - tcg_out_opc_imm(s, TARGET_LONG_BITS == 32 ? OPC_ADDIW : OPC_ADDI, + tcg_out_opc_imm(s, addr_type == TCG_TYPE_I32 ? OPC_ADDIW : OPC_ADDI, addr_adj, addr_reg, s_mask - a_mask); } compare_mask = s->page_mask | a_mask; if (compare_mask == sextreg(compare_mask, 0, 12)) { tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_adj, compare_mask); } else { - tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_TMP1, compare_mask); + tcg_out_movi(s, addr_type, TCG_REG_TMP1, compare_mask); tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP1, TCG_REG_TMP1, addr_adj); } /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP0, TCG_REG_TMP2, + tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP2, is_ld ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, @@ -1259,7 +1260,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP0, TCG_REG_TMP1, 0); /* TLB Hit - translate address using addend. */ - if (TARGET_LONG_BITS == 64) { + if (addr_type != TCG_TYPE_I32) { tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); } else if (have_zba) { tcg_out_opc_reg(s, OPC_ADD_UW, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); @@ -1287,7 +1288,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, if (guest_base != 0) { base = TCG_REG_TMP0; - if (TARGET_LONG_BITS == 64) { + if (addr_type != TCG_TYPE_I32) { tcg_out_opc_reg(s, OPC_ADD, base, addr_reg, TCG_GUEST_BASE_REG); } else if (have_zba) { tcg_out_opc_reg(s, OPC_ADD_UW, base, addr_reg, TCG_GUEST_BASE_REG); @@ -1295,7 +1296,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, tcg_out_ext32u(s, base, addr_reg); tcg_out_opc_reg(s, OPC_ADD, base, base, TCG_GUEST_BASE_REG); } - } else if (TARGET_LONG_BITS == 64) { + } else if (addr_type != TCG_TYPE_I32) { base = addr_reg; } else { base = TCG_REG_TMP0; From d588946b31109235eae641b502570ba0cf381a45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Apr 2023 16:39:09 +0100 Subject: [PATCH 073/412] tcg/s390x: Remove TARGET_LONG_BITS, TCG_TYPE_TL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All uses replaced with TCGContext.addr_type. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 503126cd66..2795242b60 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1745,6 +1745,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { + TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; @@ -1786,7 +1787,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tgen_andi_risbg(s, TCG_REG_R0, addr_reg, tlb_mask); } else { tcg_out_insn(s, RX, LA, TCG_REG_R0, addr_reg, TCG_REG_NONE, a_off); - tgen_andi(s, TCG_TYPE_TL, TCG_REG_R0, tlb_mask); + tgen_andi(s, addr_type, TCG_REG_R0, tlb_mask); } if (is_ld) { @@ -1794,7 +1795,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } else { ofs = offsetof(CPUTLBEntry, addr_write); } - if (TARGET_LONG_BITS == 32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_insn(s, RX, C, TCG_REG_R0, TCG_TMP0, TCG_REG_NONE, ofs); } else { tcg_out_insn(s, RXY, CG, TCG_REG_R0, TCG_TMP0, TCG_REG_NONE, ofs); @@ -1807,7 +1808,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_insn(s, RXY, LG, h->index, TCG_TMP0, TCG_REG_NONE, offsetof(CPUTLBEntry, addend)); - if (TARGET_LONG_BITS == 32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_insn(s, RRE, ALGFR, h->index, addr_reg); h->base = TCG_REG_NONE; } else { @@ -1830,7 +1831,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } h->base = addr_reg; - if (TARGET_LONG_BITS == 32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_ext32u(s, TCG_TMP0, addr_reg); h->base = TCG_TMP0; } From 40bca78d76cf1643e8a081ce7e52e78e408c727e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Apr 2023 18:15:07 +0100 Subject: [PATCH 074/412] tcg/sparc64: Remove TARGET_LONG_BITS, TCG_TYPE_TL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All uses replaced with TCGContext.addr_type. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target.c.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index d2d0f604c2..48efd83817 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1027,6 +1027,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { + TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; @@ -1063,7 +1064,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_arith(s, TCG_REG_T1, TCG_REG_T1, TCG_REG_T3, ARITH_ADD); /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_T2, TCG_REG_T1, cmp_off); + tcg_out_ld(s, addr_type, TCG_REG_T2, TCG_REG_T1, cmp_off); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T1, TCG_REG_T1, add_off); h->base = TCG_REG_T1; @@ -1084,7 +1085,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->label_ptr[0] = s->code_ptr; /* bne,pn %[xi]cc, label0 */ - cc = TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC; + cc = addr_type == TCG_TYPE_I32 ? BPCC_ICC : BPCC_XCC; tcg_out_bpcc0(s, COND_NE, BPCC_PN | cc, 0); #else /* @@ -1110,7 +1111,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, #endif /* If the guest address must be zero-extended, do in the delay slot. */ - if (TARGET_LONG_BITS == 32) { + if (addr_type == TCG_TYPE_I32) { tcg_out_ext32u(s, TCG_REG_T2, addr_reg); h->index = TCG_REG_T2; } else { From ff0c61bf35fbeffd5c0f85a0b67b49ccb65e04f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 28 Apr 2023 09:16:01 +0100 Subject: [PATCH 075/412] tcg: Move TCG_TYPE_TL from tcg.h to tcg-op.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the only use of TARGET_LONG_BITS from tcg.h, which is to be target independent. Move the symbol to a define in tcg-op.h, which will continue to be target dependent. Rather than complicate matters for the use in tb_gen_code(), expand the definition there. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 +- include/tcg/tcg-op.h | 8 ++++++++ include/tcg/tcg.h | 7 ------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index bf814b9e81..d7c93e3b57 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -350,7 +350,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb_set_page_addr0(tb, phys_pc); tb_set_page_addr1(tb, -1); tcg_ctx->gen_tb = tb; - tcg_ctx->addr_type = TCG_TYPE_TL; + tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; #ifdef CONFIG_SOFTMMU tcg_ctx->page_bits = TARGET_PAGE_BITS; tcg_ctx->page_mask = TARGET_PAGE_MASK; diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 35c5700183..844c666374 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -722,6 +722,14 @@ static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) #error must include QEMU headers #endif +#if TARGET_LONG_BITS == 32 +# define TCG_TYPE_TL TCG_TYPE_I32 +#elif TARGET_LONG_BITS == 64 +# define TCG_TYPE_TL TCG_TYPE_I64 +#else +# error +#endif + #if TARGET_INSN_START_WORDS == 1 static inline void tcg_gen_insn_start(target_ulong pc) { diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 072c35f7f5..0da17f1b4f 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -292,13 +292,6 @@ typedef enum TCGType { #else TCG_TYPE_PTR = TCG_TYPE_I64, #endif - - /* An alias for the size of the target "long", aka register. */ -#if TARGET_LONG_BITS == 64 - TCG_TYPE_TL = TCG_TYPE_I64, -#else - TCG_TYPE_TL = TCG_TYPE_I32, -#endif } TCGType; /** From 238f43809a85a47cfbbc2e1d6aff4640fec30328 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 Mar 2023 13:02:59 -0700 Subject: [PATCH 076/412] tcg: Widen CPUTLBEntry comparators to 64-bits This makes CPUTLBEntry agnostic to the address size of the guest. When 32-bit addresses are in effect, we can simply read the low 32 bits of the 64-bit field. Similarly when we need to update the field for setting TLB_NOTDIRTY. For TCG backends that could in theory be big-endian, but in practice are not (arm, loongarch, riscv), use QEMU_BUILD_BUG_ON to document and ensure this is not accidentally missed. For s390x, which is always big-endian, use HOST_BIG_ENDIAN anyway, to document the reason for the adjustment. For sparc64 and ppc64, always perform a 64-bit load, and rely on the following 32-bit comparison to ignore the high bits. Rearrange mips and ppc if ladders for clarity. Reviewed-by: Anton Johansson Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 8 +++++-- include/exec/cpu-defs.h | 37 +++++++++++--------------------- include/exec/cpu_ldst.h | 19 ++++++++++------ tcg/aarch64/tcg-target.c.inc | 1 + tcg/arm/tcg-target.c.inc | 1 + tcg/loongarch64/tcg-target.c.inc | 1 + tcg/mips/tcg-target.c.inc | 13 ++++++----- tcg/ppc/tcg-target.c.inc | 28 +++++++++++++----------- tcg/riscv/tcg-target.c.inc | 1 + tcg/s390x/tcg-target.c.inc | 1 + tcg/sparc64/tcg-target.c.inc | 8 +++++-- 11 files changed, 67 insertions(+), 51 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 90c72c9940..6beaeb0a81 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1000,11 +1000,15 @@ static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, addr &= TARGET_PAGE_MASK; addr += tlb_entry->addend; if ((addr - start) < length) { -#if TCG_OVERSIZED_GUEST +#if TARGET_LONG_BITS == 32 + uint32_t *ptr_write = (uint32_t *)&tlb_entry->addr_write; + ptr_write += HOST_BIG_ENDIAN; + qatomic_set(ptr_write, *ptr_write | TLB_NOTDIRTY); +#elif TCG_OVERSIZED_GUEST tlb_entry->addr_write |= TLB_NOTDIRTY; #else qatomic_set(&tlb_entry->addr_write, - tlb_entry->addr_write | TLB_NOTDIRTY); + tlb_entry->addr_write | TLB_NOTDIRTY); #endif } } diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index a6e0cf1812..b757d37966 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -65,11 +65,7 @@ /* use a fully associative victim tlb of 8 entries */ #define CPU_VTLB_SIZE 8 -#if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32 -#define CPU_TLB_ENTRY_BITS 4 -#else #define CPU_TLB_ENTRY_BITS 5 -#endif #define CPU_TLB_DYN_MIN_BITS 6 #define CPU_TLB_DYN_DEFAULT_BITS 8 @@ -95,33 +91,26 @@ # endif /* Minimalized TLB entry for use by TCG fast path. */ -typedef struct CPUTLBEntry { - /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address - bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not - go directly to ram. - bit 3 : indicates that the entry is invalid - bit 2..0 : zero - */ - union { - struct { - target_ulong addr_read; - target_ulong addr_write; - target_ulong addr_code; - /* Addend to virtual address to get host address. IO accesses - use the corresponding iotlb value. */ - uintptr_t addend; - }; +typedef union CPUTLBEntry { + struct { + uint64_t addr_read; + uint64_t addr_write; + uint64_t addr_code; /* - * Padding to get a power of two size, as well as index - * access to addr_{read,write,code}. + * Addend to virtual address to get host address. IO accesses + * use the corresponding iotlb value. */ - target_ulong addr_idx[(1 << CPU_TLB_ENTRY_BITS) / TARGET_LONG_SIZE]; + uintptr_t addend; }; + /* + * Padding to get a power of two size, as well as index + * access to addr_{read,write,code}. + */ + uint64_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uint64_t)]; } CPUTLBEntry; QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); - #endif /* !CONFIG_USER_ONLY && CONFIG_TCG */ #if !defined(CONFIG_USER_ONLY) diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 5939688f69..a43b34e46b 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -334,18 +334,25 @@ static inline target_ulong tlb_read_idx(const CPUTLBEntry *entry, { /* Do not rearrange the CPUTLBEntry structure members. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) != - MMU_DATA_LOAD * TARGET_LONG_SIZE); + MMU_DATA_LOAD * sizeof(uint64_t)); QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) != - MMU_DATA_STORE * TARGET_LONG_SIZE); + MMU_DATA_STORE * sizeof(uint64_t)); QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) != - MMU_INST_FETCH * TARGET_LONG_SIZE); + MMU_INST_FETCH * sizeof(uint64_t)); - const target_ulong *ptr = &entry->addr_idx[access_type]; -#if TCG_OVERSIZED_GUEST - return *ptr; +#if TARGET_LONG_BITS == 32 + /* Use qatomic_read, in case of addr_write; only care about low bits. */ + const uint32_t *ptr = (uint32_t *)&entry->addr_idx[access_type]; + ptr += HOST_BIG_ENDIAN; + return qatomic_read(ptr); #else + const uint64_t *ptr = &entry->addr_idx[access_type]; +# if TCG_OVERSIZED_GUEST + return *ptr; +# else /* ofs might correspond to .addr_write, so use qatomic_read */ return qatomic_read(ptr); +# endif #endif } diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 261ad25210..e23c57e2cd 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1690,6 +1690,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_insn(s, 3502, ADD, 1, TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP0); /* Load the tlb comparator into TMP0, and the fast path addend into TMP1. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP1, is_ld ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 20cc1cc477..64eb0cb5dc 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1430,6 +1430,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * Add the tlb_table pointer, creating the CPUTLBEntry address in R1. * Load the tlb comparator into R2/R3 and the fast path addend into R1. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); if (cmp_off == 0) { if (s->addr_type == TCG_TYPE_I32) { tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 0bae922982..e89f3b848b 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -875,6 +875,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_opc_add_d(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); /* Load the tlb comparator and the addend. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP2, is_ld ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 3274d9aace..4c0de0a380 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1311,14 +1311,17 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Add the tlb_table pointer, creating the CPUTLBEntry address in TMP3. */ tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); + if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { + /* Load the (low half) tlb comparator. */ + tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, + cmp_off + HOST_BIG_ENDIAN * 4); + } else { + tcg_out_ld(s, TCG_TYPE_I64, TCG_TMP0, TCG_TMP3, cmp_off); + } + if (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32) { - /* Load the tlb comparator. */ - tcg_out_ld(s, addr_type, TCG_TMP0, TCG_TMP3, cmp_off); /* Load the tlb addend for the fast path. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP3, TCG_TMP3, add_off); - } else { - /* Load the low half of the tlb comparator. */ - tcg_out_ldst(s, OPC_LW, TCG_TMP0, TCG_TMP3, cmp_off + LO_OFF); } /* diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 11955a6cc2..73f6ea0393 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2098,20 +2098,24 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, } tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); - /* Load the (low part) TLB comparator into TMP2. */ - if (cmp_off == 0 - && (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32)) { - uint32_t lxu = (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32 - ? LWZUX : LDUX); - tcg_out32(s, lxu | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); + /* + * Load the (low part) TLB comparator into TMP2. + * For 64-bit host, always load the entire 64-bit slot for simplicity. + * We will ignore the high bits with tcg_out_cmp(..., addr_type). + */ + if (TCG_TARGET_REG_BITS == 64) { + if (cmp_off == 0) { + tcg_out32(s, LDUX | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); + } else { + tcg_out32(s, ADD | TAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP2)); + tcg_out_ld(s, TCG_TYPE_I64, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); + } + } else if (cmp_off == 0 && !HOST_BIG_ENDIAN) { + tcg_out32(s, LWZUX | TAB(TCG_REG_TMP2, TCG_REG_TMP1, TCG_REG_TMP2)); } else { tcg_out32(s, ADD | TAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP2)); - if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, - TCG_REG_TMP1, cmp_off + 4 * HOST_BIG_ENDIAN); - } else { - tcg_out_ld(s, addr_type, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); - } + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, TCG_REG_TMP1, + cmp_off + 4 * HOST_BIG_ENDIAN); } /* diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index a8f99f7e77..a6d56e2d0e 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1249,6 +1249,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, } /* Load the tlb comparator and the addend. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP2, is_ld ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 2795242b60..03be800c3b 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1796,6 +1796,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ofs = offsetof(CPUTLBEntry, addr_write); } if (addr_type == TCG_TYPE_I32) { + ofs += HOST_BIG_ENDIAN * 4; tcg_out_insn(s, RX, C, TCG_REG_R0, TCG_TMP0, TCG_REG_NONE, ofs); } else { tcg_out_insn(s, RXY, CG, TCG_REG_R0, TCG_TMP0, TCG_REG_NONE, ofs); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 48efd83817..6c60657c36 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1063,8 +1063,12 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Add the tlb_table pointer, creating the CPUTLBEntry address into R2. */ tcg_out_arith(s, TCG_REG_T1, TCG_REG_T1, TCG_REG_T3, ARITH_ADD); - /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, addr_type, TCG_REG_T2, TCG_REG_T1, cmp_off); + /* + * Load the tlb comparator and the addend. + * Always load the entire 64-bit comparator for simplicity. + * We will ignore the high bits via BPCC_ICC below. + */ + tcg_out_ld(s, TCG_TYPE_I64, TCG_REG_T2, TCG_REG_T1, cmp_off); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T1, TCG_REG_T1, add_off); h->base = TCG_REG_T1; From d0a9bb5ecb85383198fb416bb8ecfd11127e6452 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 16:07:15 -0700 Subject: [PATCH 077/412] tcg: Add tlb_fast_offset to TCGContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disconnect the layout of ArchCPU from TCG compilation. Pass the relative offset of 'env' and 'neg.tlb.f' as a parameter. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 ++ include/exec/cpu-defs.h | 39 +--------------------- include/exec/tlb-common.h | 56 ++++++++++++++++++++++++++++++++ include/tcg/tcg.h | 1 + tcg/aarch64/tcg-target.c.inc | 7 ++-- tcg/arm/tcg-target.c.inc | 7 ++-- tcg/i386/tcg-target.c.inc | 9 ++--- tcg/loongarch64/tcg-target.c.inc | 7 ++-- tcg/mips/tcg-target.c.inc | 7 ++-- tcg/ppc/tcg-target.c.inc | 7 ++-- tcg/riscv/tcg-target.c.inc | 7 ++-- tcg/s390x/tcg-target.c.inc | 7 ++-- tcg/sparc64/tcg-target.c.inc | 7 ++-- tcg/tcg.c | 13 ++++++++ 14 files changed, 110 insertions(+), 66 deletions(-) create mode 100644 include/exec/tlb-common.h diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index d7c93e3b57..5cea9acd5a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -355,6 +355,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->page_bits = TARGET_PAGE_BITS; tcg_ctx->page_mask = TARGET_PAGE_MASK; tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; + tcg_ctx->tlb_fast_offset = + (int)offsetof(ArchCPU, neg.tlb.f) - (int)offsetof(ArchCPU, env); #endif tb_overflow: diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index b757d37966..0d418a0384 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -61,12 +61,11 @@ #define NB_MMU_MODES 16 #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) +#include "exec/tlb-common.h" /* use a fully associative victim tlb of 8 entries */ #define CPU_VTLB_SIZE 8 -#define CPU_TLB_ENTRY_BITS 5 - #define CPU_TLB_DYN_MIN_BITS 6 #define CPU_TLB_DYN_DEFAULT_BITS 8 @@ -90,27 +89,6 @@ # endif # endif -/* Minimalized TLB entry for use by TCG fast path. */ -typedef union CPUTLBEntry { - struct { - uint64_t addr_read; - uint64_t addr_write; - uint64_t addr_code; - /* - * Addend to virtual address to get host address. IO accesses - * use the corresponding iotlb value. - */ - uintptr_t addend; - }; - /* - * Padding to get a power of two size, as well as index - * access to addr_{read,write,code}. - */ - uint64_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uint64_t)]; -} CPUTLBEntry; - -QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); - #endif /* !CONFIG_USER_ONLY && CONFIG_TCG */ #if !defined(CONFIG_USER_ONLY) @@ -184,17 +162,6 @@ typedef struct CPUTLBDesc { CPUTLBEntryFull *fulltlb; } CPUTLBDesc; -/* - * Data elements that are per MMU mode, accessed by the fast path. - * The structure is aligned to aid loading the pair with one insn. - */ -typedef struct CPUTLBDescFast { - /* Contains (n_entries - 1) << CPU_TLB_ENTRY_BITS */ - uintptr_t mask; - /* The array of tlb entries itself. */ - CPUTLBEntry *table; -} CPUTLBDescFast QEMU_ALIGNED(2 * sizeof(void *)); - /* * Data elements that are shared between all MMU modes. */ @@ -230,10 +197,6 @@ typedef struct CPUTLB { CPUTLBDescFast f[NB_MMU_MODES]; } CPUTLB; -/* This will be used by TCG backends to compute offsets. */ -#define TLB_MASK_TABLE_OFS(IDX) \ - ((int)offsetof(ArchCPU, neg.tlb.f[IDX]) - (int)offsetof(ArchCPU, env)) - #else typedef struct CPUTLB { } CPUTLB; diff --git a/include/exec/tlb-common.h b/include/exec/tlb-common.h new file mode 100644 index 0000000000..dc5a5faa0b --- /dev/null +++ b/include/exec/tlb-common.h @@ -0,0 +1,56 @@ +/* + * Common definitions for the softmmu tlb + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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.1 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 EXEC_TLB_COMMON_H +#define EXEC_TLB_COMMON_H 1 + +#define CPU_TLB_ENTRY_BITS 5 + +/* Minimalized TLB entry for use by TCG fast path. */ +typedef union CPUTLBEntry { + struct { + uint64_t addr_read; + uint64_t addr_write; + uint64_t addr_code; + /* + * Addend to virtual address to get host address. IO accesses + * use the corresponding iotlb value. + */ + uintptr_t addend; + }; + /* + * Padding to get a power of two size, as well as index + * access to addr_{read,write,code}. + */ + uint64_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uint64_t)]; +} CPUTLBEntry; + +QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); + +/* + * Data elements that are per MMU mode, accessed by the fast path. + * The structure is aligned to aid loading the pair with one insn. + */ +typedef struct CPUTLBDescFast { + /* Contains (n_entries - 1) << CPU_TLB_ENTRY_BITS */ + uintptr_t mask; + /* The array of tlb entries itself. */ + CPUTLBEntry *table; +} CPUTLBDescFast QEMU_ALIGNED(2 * sizeof(void *)); + +#endif /* EXEC_TLB_COMMON_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 0da17f1b4f..54f260a66b 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -547,6 +547,7 @@ struct TCGContext { TCGType addr_type; /* TCG_TYPE_I32 or TCG_TYPE_I64 */ #ifdef CONFIG_SOFTMMU + int tlb_fast_offset; int page_mask; uint8_t page_bits; uint8_t tlb_dyn_max_bits; diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index e23c57e2cd..35ca80cd56 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1636,6 +1636,9 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) return true; } +/* We expect to use a 7-bit scaled negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -512 + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -1674,12 +1677,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ? TCG_TYPE_I64 : TCG_TYPE_I32); /* Load env_tlb(env)->f[mmu_idx].{mask,table} into {tmp0,tmp1}. */ - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -512); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 8); tcg_out_insn(s, 3314, LDP, TCG_REG_TMP0, TCG_REG_TMP1, TCG_AREG0, - TLB_MASK_TABLE_OFS(mem_index), 1, 0); + tlb_mask_table_ofs(s, mem_index), 1, 0); /* Extract the TLB index from the address into X0. */ tcg_out_insn(s, 3502S, AND_LSR, mask_type == TCG_TYPE_I64, diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 64eb0cb5dc..83e286088f 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1374,6 +1374,9 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) return true; } +/* We expect to use an 9-bit sign-magnitude negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -256 + static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, TCGReg addrlo, TCGReg addrhi, MemOpIdx oi, bool is_ld) @@ -1405,7 +1408,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, int mem_index = get_mmuidx(oi); int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); + int fast_off = tlb_mask_table_ofs(s, mem_index); unsigned s_mask = (1 << (opc & MO_SIZE)) - 1; TCGReg t_addr; @@ -1416,8 +1419,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->addrhi_reg = addrhi; /* Load env_tlb(env)->f[mmu_idx].{mask,table} into {r0,r1}. */ - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -256); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 4); tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index ae54e5fbf3..ab997b5fb3 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1900,6 +1900,8 @@ static inline int setup_guest_base_seg(void) #endif /* setup_guest_base_seg */ #endif /* !SOFTMMU */ +#define MIN_TLB_MASK_TABLE_OFS INT_MIN + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -1934,6 +1936,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, int trexw = 0, hrexw = 0, tlbrexw = 0; unsigned mem_index = get_mmuidx(oi); unsigned s_mask = (1 << s_bits) - 1; + int fast_ofs = tlb_mask_table_ofs(s, mem_index); int tlb_mask; ldst = new_ldst_label(s); @@ -1959,12 +1962,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, s->page_bits - CPU_TLB_ENTRY_BITS); tcg_out_modrm_offset(s, OPC_AND_GvEv + trexw, TCG_REG_L0, TCG_AREG0, - TLB_MASK_TABLE_OFS(mem_index) + - offsetof(CPUTLBDescFast, mask)); + fast_ofs + offsetof(CPUTLBDescFast, mask)); tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, TCG_REG_L0, TCG_AREG0, - TLB_MASK_TABLE_OFS(mem_index) + - offsetof(CPUTLBDescFast, table)); + fast_ofs + offsetof(CPUTLBDescFast, table)); /* * If the required alignment is at least as large as the access, simply diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e89f3b848b..baf5fc3819 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -834,6 +834,9 @@ bool tcg_target_has_memory_bswap(MemOp memop) return false; } +/* We expect to use a 12-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 11) + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -855,7 +858,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, #ifdef CONFIG_SOFTMMU unsigned s_bits = opc & MO_SIZE; int mem_index = get_mmuidx(oi); - int fast_ofs = TLB_MASK_TABLE_OFS(mem_index); + int fast_ofs = tlb_mask_table_ofs(s, mem_index); int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask); int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table); @@ -864,8 +867,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->oi = oi; ldst->addrlo_reg = addr_reg; - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 11)); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 4c0de0a380..9faa8bdf0b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1254,6 +1254,9 @@ bool tcg_target_has_memory_bswap(MemOp memop) return false; } +/* We expect to use a 16-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -32768 + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -1279,7 +1282,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, #ifdef CONFIG_SOFTMMU unsigned s_mask = (1 << s_bits) - 1; int mem_index = get_mmuidx(oi); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); + int fast_off = tlb_mask_table_ofs(s, mem_index); int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); int table_off = fast_off + offsetof(CPUTLBDescFast, table); int add_off = offsetof(CPUTLBEntry, addend); @@ -1293,8 +1296,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->addrhi_reg = addrhi; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -32768); tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_AREG0, mask_off); tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP1, TCG_AREG0, table_off); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 73f6ea0393..507fe6cda8 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2036,6 +2036,9 @@ bool tcg_target_has_memory_bswap(MemOp memop) return aa.atom <= MO_64; } +/* We expect to use a 16-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -32768 + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -2072,7 +2075,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, int mem_index = get_mmuidx(oi); int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); + int fast_off = tlb_mask_table_ofs(s, mem_index); int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); int table_off = fast_off + offsetof(CPUTLBDescFast, table); @@ -2083,8 +2086,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->addrhi_reg = addrhi; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -32768); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, mask_off); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_AREG0, table_off); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index a6d56e2d0e..eeaeb6b6e3 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1185,6 +1185,9 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) return true; } +/* We expect to use a 12-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 11) + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -1208,7 +1211,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, unsigned s_bits = opc & MO_SIZE; unsigned s_mask = (1u << s_bits) - 1; int mem_index = get_mmuidx(oi); - int fast_ofs = TLB_MASK_TABLE_OFS(mem_index); + int fast_ofs = tlb_mask_table_ofs(s, mem_index); int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask); int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table); int compare_mask; @@ -1219,8 +1222,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, ldst->oi = oi; ldst->addrlo_reg = addr_reg; - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 11)); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 03be800c3b..aeddebbb5c 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1735,6 +1735,9 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) return true; } +/* We're expecting to use a 20-bit negative offset on the tlb memory ops. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 19) + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -1757,7 +1760,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, #ifdef CONFIG_SOFTMMU unsigned s_mask = (1 << s_bits) - 1; int mem_index = get_mmuidx(oi); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); + int fast_off = tlb_mask_table_ofs(s, mem_index); int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); int table_off = fast_off + offsetof(CPUTLBDescFast, table); int ofs, a_off; @@ -1771,8 +1774,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_sh64(s, RSY_SRLG, TCG_TMP0, addr_reg, TCG_REG_NONE, s->page_bits - CPU_TLB_ENTRY_BITS); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 19)); tcg_out_insn(s, RXY, NG, TCG_TMP0, TCG_AREG0, TCG_REG_NONE, mask_off); tcg_out_insn(s, RXY, AG, TCG_TMP0, TCG_AREG0, TCG_REG_NONE, table_off); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 6c60657c36..ffcb879211 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1017,6 +1017,9 @@ bool tcg_target_has_memory_bswap(MemOp memop) return true; } +/* We expect to use a 13-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 12) + /* * For softmmu, perform the TLB load and compare. * For useronly, perform any required alignment tests. @@ -1040,7 +1043,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, #ifdef CONFIG_SOFTMMU int mem_index = get_mmuidx(oi); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); + int fast_off = tlb_mask_table_ofs(s, mem_index); int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); int table_off = fast_off + offsetof(CPUTLBDescFast, table); int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) @@ -1050,8 +1053,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, int cc; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); - QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 12)); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T2, TCG_AREG0, mask_off); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T3, TCG_AREG0, table_off); diff --git a/tcg/tcg.c b/tcg/tcg.c index 2352ca4ade..2d17c09aea 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -41,6 +41,7 @@ #define NO_CPU_IO_DEFS #include "exec/exec-all.h" +#include "exec/tlb-common.h" #include "tcg/tcg-op.h" #if UINTPTR_MAX == UINT32_MAX @@ -407,6 +408,13 @@ static uintptr_t G_GNUC_UNUSED get_jmp_target_addr(TCGContext *s, int which) return (uintptr_t)tcg_splitwx_to_rx(&s->gen_tb->jmp_target_addr[which]); } +#if defined(CONFIG_SOFTMMU) && !defined(CONFIG_TCG_INTERPRETER) +static int tlb_mask_table_ofs(TCGContext *s, int which) +{ + return s->tlb_fast_offset + which * sizeof(CPUTLBDescFast); +} +#endif + /* Signal overflow, starting over with fewer guest insns. */ static G_NORETURN void tcg_raise_tb_overflow(TCGContext *s) @@ -1521,6 +1529,11 @@ void tcg_func_start(TCGContext *s) tcg_debug_assert(s->addr_type == TCG_TYPE_I32 || s->addr_type == TCG_TYPE_I64); + +#if defined(CONFIG_SOFTMMU) && !defined(CONFIG_TCG_INTERPRETER) + tcg_debug_assert(s->tlb_fast_offset < 0); + tcg_debug_assert(s->tlb_fast_offset >= MIN_TLB_MASK_TABLE_OFS); +#endif } static TCGTemp *tcg_temp_alloc(TCGContext *s) From acf2f8595aaa3b1c0993e1ea17e233b59f9e87a7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 18:21:42 -0700 Subject: [PATCH 078/412] target/avr: Add missing includes of qemu/error-report.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been pulled in from tcg/tcg.h, via exec/cpu_ldst.h, via exec/exec-all.h, but the include of tcg.h will be removed. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/avr/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/avr/helper.c b/target/avr/helper.c index 156dde4e92..2bad242a66 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/error-report.h" #include "cpu.h" #include "hw/core/tcg-cpu-ops.h" #include "exec/exec-all.h" From fafe0021e32d339e64d6042811640d66c8336d4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 18:23:15 -0700 Subject: [PATCH 079/412] target/*: Add missing includes of tcg/debug-assert.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been pulled in from tcg/tcg.h, via exec/cpu_ldst.h, via exec/exec-all.h, but the include of tcg.h will be removed. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/avr/cpu.c | 1 + target/rx/cpu.c | 1 + target/rx/op_helper.c | 1 + target/tricore/cpu.c | 1 + 4 files changed, 4 insertions(+) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index a24c23c247..8f741f258c 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "cpu.h" #include "disas/dis-asm.h" +#include "tcg/debug-assert.h" static void avr_cpu_set_pc(CPUState *cs, vaddr value) { diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 67452e310c..157e57da0f 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "hw/loader.h" #include "fpu/softfloat.h" +#include "tcg/debug-assert.h" static void rx_cpu_set_pc(CPUState *cs, vaddr value) { diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index acce650185..dc0092ca99 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -23,6 +23,7 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "fpu/softfloat.h" +#include "tcg/debug-assert.h" static inline G_NORETURN void raise_exception(CPURXState *env, int index, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index d0a9272961..7fa113fed2 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "qemu/error-report.h" +#include "tcg/debug-assert.h" static inline void set_feature(CPUTriCoreState *env, int feature) { From e5b490637708a688e303a51d47fea3bd14ec98f6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 18:24:50 -0700 Subject: [PATCH 080/412] *: Add missing includes of tcg/tcg.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been pulled in from exec/cpu_ldst.h, via exec/exec-all.h, but the include of tcg.h will be removed. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/monitor.c | 1 + accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 2 +- target/i386/helper.c | 3 +++ target/openrisc/sys_helper.c | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 92fce580f1..f171bc6f5e 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -15,6 +15,7 @@ #include "sysemu/cpus.h" #include "sysemu/cpu-timers.h" #include "sysemu/tcg.h" +#include "tcg/tcg.h" #include "internal.h" diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index d50239e0e2..5d72c9b1bd 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -32,7 +32,7 @@ #include "qemu/guest-random.h" #include "exec/exec-all.h" #include "hw/boards.h" - +#include "tcg/tcg.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index b6d10fa9a2..70b9b89073 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -32,7 +32,7 @@ #include "qemu/notify.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" - +#include "tcg/tcg.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-rr.h" #include "tcg-accel-ops-icount.h" diff --git a/target/i386/helper.c b/target/i386/helper.c index 8857444819..682d10d98a 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -28,6 +28,9 @@ #include "monitor/monitor.h" #endif #include "qemu/log.h" +#ifdef CONFIG_TCG +#include "tcg/tcg.h" +#endif void cpu_sync_avx_hflag(CPUX86State *env) { diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index ccdee3b8be..110f157601 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -26,6 +26,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #endif +#include "tcg/tcg.h" #define TO_SPR(group, number) (((group) << 11) + (number)) From d46259c037e51fb6516199305fe8f0994df3d46e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 1 May 2023 10:57:11 +0100 Subject: [PATCH 081/412] tcg: Split out tcg-target-reg-bits.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Often, the only thing we need to know about the TCG host is the register size. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 12 +----------- tcg/aarch64/tcg-target-reg-bits.h | 12 ++++++++++++ tcg/arm/tcg-target-reg-bits.h | 12 ++++++++++++ tcg/i386/tcg-target-reg-bits.h | 16 ++++++++++++++++ tcg/i386/tcg-target.h | 2 -- tcg/loongarch64/tcg-target-reg-bits.h | 21 +++++++++++++++++++++ tcg/loongarch64/tcg-target.h | 11 ----------- tcg/mips/tcg-target-reg-bits.h | 18 ++++++++++++++++++ tcg/mips/tcg-target.h | 8 -------- tcg/ppc/tcg-target-reg-bits.h | 16 ++++++++++++++++ tcg/ppc/tcg-target.h | 5 ----- tcg/riscv/tcg-target-reg-bits.h | 19 +++++++++++++++++++ tcg/riscv/tcg-target.h | 9 --------- tcg/s390x/tcg-target-reg-bits.h | 17 +++++++++++++++++ tcg/s390x/tcg-target.c.inc | 5 ----- tcg/sparc64/tcg-target-reg-bits.h | 12 ++++++++++++ tcg/tci/tcg-target-reg-bits.h | 18 ++++++++++++++++++ tcg/tci/tcg-target.h | 8 -------- 18 files changed, 162 insertions(+), 59 deletions(-) create mode 100644 tcg/aarch64/tcg-target-reg-bits.h create mode 100644 tcg/arm/tcg-target-reg-bits.h create mode 100644 tcg/i386/tcg-target-reg-bits.h create mode 100644 tcg/loongarch64/tcg-target-reg-bits.h create mode 100644 tcg/mips/tcg-target-reg-bits.h create mode 100644 tcg/ppc/tcg-target-reg-bits.h create mode 100644 tcg/riscv/tcg-target-reg-bits.h create mode 100644 tcg/s390x/tcg-target-reg-bits.h create mode 100644 tcg/sparc64/tcg-target-reg-bits.h create mode 100644 tcg/tci/tcg-target-reg-bits.h diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 54f260a66b..5fe90cbb42 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -32,6 +32,7 @@ #include "qemu/plugin.h" #include "qemu/queue.h" #include "tcg/tcg-mo.h" +#include "tcg-target-reg-bits.h" #include "tcg-target.h" #include "tcg/tcg-cond.h" #include "tcg/debug-assert.h" @@ -44,17 +45,6 @@ #define CPU_TEMP_BUF_NLONGS 128 #define TCG_STATIC_FRAME_SIZE (CPU_TEMP_BUF_NLONGS * sizeof(long)) -/* Default target word size to pointer size. */ -#ifndef TCG_TARGET_REG_BITS -# if UINTPTR_MAX == UINT32_MAX -# define TCG_TARGET_REG_BITS 32 -# elif UINTPTR_MAX == UINT64_MAX -# define TCG_TARGET_REG_BITS 64 -# else -# error Unknown pointer size for tcg target -# endif -#endif - #if TCG_TARGET_REG_BITS == 32 typedef int32_t tcg_target_long; typedef uint32_t tcg_target_ulong; diff --git a/tcg/aarch64/tcg-target-reg-bits.h b/tcg/aarch64/tcg-target-reg-bits.h new file mode 100644 index 0000000000..3b57a1aafb --- /dev/null +++ b/tcg/aarch64/tcg-target-reg-bits.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Define target-specific register size + * Copyright (c) 2023 Linaro + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#define TCG_TARGET_REG_BITS 64 + +#endif diff --git a/tcg/arm/tcg-target-reg-bits.h b/tcg/arm/tcg-target-reg-bits.h new file mode 100644 index 0000000000..23b7730a8d --- /dev/null +++ b/tcg/arm/tcg-target-reg-bits.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2023 Linaro + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#define TCG_TARGET_REG_BITS 32 + +#endif diff --git a/tcg/i386/tcg-target-reg-bits.h b/tcg/i386/tcg-target-reg-bits.h new file mode 100644 index 0000000000..aa386050eb --- /dev/null +++ b/tcg/i386/tcg-target-reg-bits.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#ifdef __x86_64__ +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif + +#endif diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 1468f8ef25..2a2e3fffa8 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -30,11 +30,9 @@ #define TCG_TARGET_INSN_UNIT_SIZE 1 #ifdef __x86_64__ -# define TCG_TARGET_REG_BITS 64 # define TCG_TARGET_NB_REGS 32 # define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) #else -# define TCG_TARGET_REG_BITS 32 # define TCG_TARGET_NB_REGS 24 # define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX #endif diff --git a/tcg/loongarch64/tcg-target-reg-bits.h b/tcg/loongarch64/tcg-target-reg-bits.h new file mode 100644 index 0000000000..51373ad70a --- /dev/null +++ b/tcg/loongarch64/tcg-target-reg-bits.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2021 WANG Xuerui + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +/* + * Loongson removed the (incomplete) 32-bit support from kernel and toolchain + * for the initial upstreaming of this architecture, so don't bother and just + * support the LP64* ABI for now. + */ +#if defined(__loongarch64) +# define TCG_TARGET_REG_BITS 64 +#else +# error unsupported LoongArch register size +#endif + +#endif diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 482901ac15..26f1aab780 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -29,17 +29,6 @@ #ifndef LOONGARCH_TCG_TARGET_H #define LOONGARCH_TCG_TARGET_H -/* - * Loongson removed the (incomplete) 32-bit support from kernel and toolchain - * for the initial upstreaming of this architecture, so don't bother and just - * support the LP64* ABI for now. - */ -#if defined(__loongarch64) -# define TCG_TARGET_REG_BITS 64 -#else -# error unsupported LoongArch register size -#endif - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 32 diff --git a/tcg/mips/tcg-target-reg-bits.h b/tcg/mips/tcg-target-reg-bits.h new file mode 100644 index 0000000000..56fe0a725e --- /dev/null +++ b/tcg/mips/tcg-target-reg-bits.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2008-2009 Arnaud Patard + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#if _MIPS_SIM == _ABIO32 +# define TCG_TARGET_REG_BITS 32 +#elif _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 +# define TCG_TARGET_REG_BITS 64 +#else +# error "Unknown ABI" +#endif + +#endif diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index e4806f6ff5..dd2efa795c 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -27,14 +27,6 @@ #ifndef MIPS_TCG_TARGET_H #define MIPS_TCG_TARGET_H -#if _MIPS_SIM == _ABIO32 -# define TCG_TARGET_REG_BITS 32 -#elif _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 -# define TCG_TARGET_REG_BITS 64 -#else -# error "Unknown ABI" -#endif - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 32 diff --git a/tcg/ppc/tcg-target-reg-bits.h b/tcg/ppc/tcg-target-reg-bits.h new file mode 100644 index 0000000000..0efa80e7e0 --- /dev/null +++ b/tcg/ppc/tcg-target-reg-bits.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#ifdef _ARCH_PPC64 +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif + +#endif diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 40f20b0c1a..c7552b6391 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -25,11 +25,6 @@ #ifndef PPC_TCG_TARGET_H #define PPC_TCG_TARGET_H -#ifdef _ARCH_PPC64 -# define TCG_TARGET_REG_BITS 64 -#else -# define TCG_TARGET_REG_BITS 32 -#endif #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) #define TCG_TARGET_NB_REGS 64 diff --git a/tcg/riscv/tcg-target-reg-bits.h b/tcg/riscv/tcg-target-reg-bits.h new file mode 100644 index 0000000000..761ca0d774 --- /dev/null +++ b/tcg/riscv/tcg-target-reg-bits.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2018 SiFive, Inc + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +/* + * We don't support oversize guests. + * Since we will only build tcg once, this in turn requires a 64-bit host. + */ +#if __riscv_xlen != 64 +#error "unsupported code generation mode" +#endif +#define TCG_TARGET_REG_BITS 64 + +#endif diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 54fdff0caa..e1d8110ee4 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -25,15 +25,6 @@ #ifndef RISCV_TCG_TARGET_H #define RISCV_TCG_TARGET_H -/* - * We don't support oversize guests. - * Since we will only build tcg once, this in turn requires a 64-bit host. - */ -#if __riscv_xlen != 64 -#error "unsupported code generation mode" -#endif -#define TCG_TARGET_REG_BITS 64 - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 32 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) diff --git a/tcg/s390x/tcg-target-reg-bits.h b/tcg/s390x/tcg-target-reg-bits.h new file mode 100644 index 0000000000..b01414e09d --- /dev/null +++ b/tcg/s390x/tcg-target-reg-bits.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2009 Ulrich Hecht + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +/* We only support generating code for 64-bit mode. */ +#if UINTPTR_MAX == UINT64_MAX +# define TCG_TARGET_REG_BITS 64 +#else +# error "unsupported code generation mode" +#endif + +#endif diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index aeddebbb5c..a878acd8ca 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -24,11 +24,6 @@ * THE SOFTWARE. */ -/* We only support generating code for 64-bit mode. */ -#if TCG_TARGET_REG_BITS != 64 -#error "unsupported code generation mode" -#endif - #include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #include "elf.h" diff --git a/tcg/sparc64/tcg-target-reg-bits.h b/tcg/sparc64/tcg-target-reg-bits.h new file mode 100644 index 0000000000..34a6711013 --- /dev/null +++ b/tcg/sparc64/tcg-target-reg-bits.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2023 Linaro + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#define TCG_TARGET_REG_BITS 64 + +#endif diff --git a/tcg/tci/tcg-target-reg-bits.h b/tcg/tci/tcg-target-reg-bits.h new file mode 100644 index 0000000000..dcb1a203f8 --- /dev/null +++ b/tcg/tci/tcg-target-reg-bits.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2009, 2011 Stefan Weil + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#if UINTPTR_MAX == UINT32_MAX +# define TCG_TARGET_REG_BITS 32 +#elif UINTPTR_MAX == UINT64_MAX +# define TCG_TARGET_REG_BITS 64 +#else +# error Unknown pointer size for tci target +#endif + +#endif diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 60a6ed65ce..37ee10c959 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -44,14 +44,6 @@ #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) -#if UINTPTR_MAX == UINT32_MAX -# define TCG_TARGET_REG_BITS 32 -#elif UINTPTR_MAX == UINT64_MAX -# define TCG_TARGET_REG_BITS 64 -#else -# error Unknown pointer size for tci target -#endif - /* Optional instructions. */ #define TCG_TARGET_HAS_bswap16_i32 1 From d3ae5f5d4faf535aa50243241e48dddb488cfa9c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 18:30:15 -0700 Subject: [PATCH 082/412] target/arm: Fix test of TCG_OVERSIZED_GUEST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The symbol is always defined, even if to 0. We wanted to test for TCG_OVERSIZED_GUEST == 0. This fixed, the #error is reached while building arm-softmmu, because TCG_OVERSIZED_GUEST is not true (nor supposed to be true) for arm32 guest on a 32-bit host. But that's ok, because this feature doesn't apply to arm32. Add an #ifdef for TARGET_AARCH64. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/ptw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 69c05cd9da..b0d2a05403 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -418,6 +418,7 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, uint64_t new_val, S1Translate *ptw, ARMMMUFaultInfo *fi) { +#ifdef TARGET_AARCH64 uint64_t cur_val; void *host = ptw->out_host; @@ -473,7 +474,7 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, * we know that TCG_OVERSIZED_GUEST is set, which means that we are * running in round-robin mode and could only race with dma i/o. */ -#ifndef TCG_OVERSIZED_GUEST +#if !TCG_OVERSIZED_GUEST # error "Unexpected configuration" #endif bool locked = qemu_mutex_iothread_locked(); @@ -497,6 +498,10 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, #endif return cur_val; +#else + /* AArch32 does not have FEAT_HADFS. */ + g_assert_not_reached(); +#endif } static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, From 70f168f88cab3ee8b377e90cad398e69c3deafa4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 18:32:36 -0700 Subject: [PATCH 083/412] tcg: Split out tcg/oversized-guest.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move a use of TARGET_LONG_BITS out of tcg/tcg.h. Include the new file only where required. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 1 + accel/tcg/tcg-all.c | 1 + include/exec/cpu_ldst.h | 3 +-- include/tcg/oversized-guest.h | 23 +++++++++++++++++++++++ include/tcg/tcg.h | 9 --------- target/arm/ptw.c | 1 + target/riscv/cpu_helper.c | 1 + 7 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 include/tcg/oversized-guest.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 6beaeb0a81..32a4817139 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -40,6 +40,7 @@ #include "qemu/plugin-memory.h" #endif #include "tcg/tcg-ldst.h" +#include "tcg/oversized-guest.h" #include "exec/helper-proto.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index a831f8d7c3..02af6a2891 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -28,6 +28,7 @@ #include "exec/replay-core.h" #include "sysemu/cpu-timers.h" #include "tcg/tcg.h" +#include "tcg/oversized-guest.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/accel.h" diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index a43b34e46b..896f305ff3 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -326,8 +326,7 @@ static inline void clear_helper_retaddr(void) #else -/* Needed for TCG_OVERSIZED_GUEST */ -#include "tcg/tcg.h" +#include "tcg/oversized-guest.h" static inline target_ulong tlb_read_idx(const CPUTLBEntry *entry, MMUAccessType access_type) diff --git a/include/tcg/oversized-guest.h b/include/tcg/oversized-guest.h new file mode 100644 index 0000000000..641b9749ff --- /dev/null +++ b/include/tcg/oversized-guest.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define TCG_OVERSIZED_GUEST + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef EXEC_TCG_OVERSIZED_GUEST_H +#define EXEC_TCG_OVERSIZED_GUEST_H + +#include "tcg-target-reg-bits.h" +#include "cpu-param.h" + +/* + * Oversized TCG guests make things like MTTCG hard + * as we can't use atomics for cputlb updates. + */ +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS +#define TCG_OVERSIZED_GUEST 1 +#else +#define TCG_OVERSIZED_GUEST 0 +#endif + +#endif diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 5fe90cbb42..021fc903ad 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -59,15 +59,6 @@ typedef uint64_t tcg_target_ulong; #error unsupported #endif -/* Oversized TCG guests make things like MTTCG hard - * as we can't use atomics for cputlb updates. - */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -#define TCG_OVERSIZED_GUEST 1 -#else -#define TCG_OVERSIZED_GUEST 0 -#endif - #if TCG_TARGET_NB_REGS <= 32 typedef uint32_t TCGRegSet; #elif TCG_TARGET_NB_REGS <= 64 diff --git a/target/arm/ptw.c b/target/arm/ptw.c index b0d2a05403..b2dc223525 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -14,6 +14,7 @@ #include "cpu.h" #include "internals.h" #include "idau.h" +#include "tcg/oversized-guest.h" typedef struct S1Translate { diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 57d04385f1..56381aaf26 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -31,6 +31,7 @@ #include "sysemu/cpu-timers.h" #include "cpu_bits.h" #include "debug.h" +#include "tcg/oversized-guest.h" int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) { From f15f8935b0ca829f612aeb7e9229508c19819b84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 Mar 2023 18:44:05 -0700 Subject: [PATCH 084/412] tcg: Move TCGv, dup_const_tl definitions to tcg-op.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two items are the last uses of TARGET_LONG_BITS within tcg.h, and are more in common with the other "_tl" definitions within that file. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-op.h | 15 ++++++++++++++- include/tcg/tcg.h | 19 ------------------- target/mips/tcg/translate.h | 1 + 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 844c666374..b8f0599f3c 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -808,19 +808,23 @@ static inline void tcg_gen_plugin_cb_end(void) } #if TARGET_LONG_BITS == 32 +typedef TCGv_i32 TCGv; #define tcg_temp_new() tcg_temp_new_i32() #define tcg_global_mem_new tcg_global_mem_new_i32 #define tcg_temp_free tcg_temp_free_i32 #define tcgv_tl_temp tcgv_i32_temp #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i32 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i32 -#else +#elif TARGET_LONG_BITS == 64 +typedef TCGv_i64 TCGv; #define tcg_temp_new() tcg_temp_new_i64() #define tcg_global_mem_new tcg_global_mem_new_i64 #define tcg_temp_free tcg_temp_free_i64 #define tcgv_tl_temp tcgv_i64_temp #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i64 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i64 +#else +#error Unhandled TARGET_LONG_BITS value #endif void tcg_gen_qemu_ld_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); @@ -1182,6 +1186,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i64 #define tcg_gen_dup_tl_vec tcg_gen_dup_i64_vec #define tcg_gen_dup_tl tcg_gen_dup_i64 +#define dup_const_tl dup_const #else #define tcg_gen_movi_tl tcg_gen_movi_i32 #define tcg_gen_mov_tl tcg_gen_mov_i32 @@ -1296,6 +1301,14 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i32 #define tcg_gen_dup_tl_vec tcg_gen_dup_i32_vec #define tcg_gen_dup_tl tcg_gen_dup_i32 + +#define dup_const_tl(VECE, C) \ + (__builtin_constant_p(VECE) \ + ? ( (VECE) == MO_8 ? 0x01010101ul * (uint8_t)(C) \ + : (VECE) == MO_16 ? 0x00010001ul * (uint16_t)(C) \ + : (VECE) == MO_32 ? 0x00000001ul * (uint32_t)(C) \ + : (qemu_build_not_reached_always(), 0)) \ + : (target_long)dup_const(VECE, C)) #endif #if UINTPTR_MAX == UINT32_MAX diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 021fc903ad..9b2833b31d 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -357,13 +357,6 @@ typedef struct TCGv_i128_d *TCGv_i128; typedef struct TCGv_ptr_d *TCGv_ptr; typedef struct TCGv_vec_d *TCGv_vec; typedef TCGv_ptr TCGv_env; -#if TARGET_LONG_BITS == 32 -#define TCGv TCGv_i32 -#elif TARGET_LONG_BITS == 64 -#define TCGv TCGv_i64 -#else -#error Unhandled TARGET_LONG_BITS value -#endif /* call flags */ /* Helper does not read globals (either directly or through an exception). It @@ -1163,18 +1156,6 @@ uint64_t dup_const(unsigned vece, uint64_t c); : (qemu_build_not_reached_always(), 0)) \ : dup_const(VECE, C)) -#if TARGET_LONG_BITS == 64 -# define dup_const_tl dup_const -#else -# define dup_const_tl(VECE, C) \ - (__builtin_constant_p(VECE) \ - ? ( (VECE) == MO_8 ? 0x01010101ul * (uint8_t)(C) \ - : (VECE) == MO_16 ? 0x00010001ul * (uint16_t)(C) \ - : (VECE) == MO_32 ? 0x00000001ul * (uint32_t)(C) \ - : (qemu_build_not_reached_always(), 0)) \ - : (target_long)dup_const(VECE, C)) -#endif - #ifdef CONFIG_DEBUG_TCG void tcg_assert_listed_vecop(TCGOpcode); #else diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 69f85841d2..fa8bf55209 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -10,6 +10,7 @@ #include "qemu/log.h" #include "exec/translator.h" +#include "tcg/tcg-op.h" #define MIPS_DEBUG_DISAS 0 From ad3d0e4d5dcbd3dd43686b23ce4dd33d921b40df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 Mar 2023 18:17:24 -0700 Subject: [PATCH 085/412] tcg: Split tcg/tcg-op-common.h from tcg/tcg-op.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create tcg/tcg-op-common.h, moving everything that does not concern TARGET_LONG_BITS or TCGv. Adjust tcg/*.c to use the new header instead of tcg-op.h, in preparation for compiling tcg/ only once. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-op-common.h | 996 ++++++++++++++++++++++++++++++++++ include/tcg/tcg-op.h | 1004 +---------------------------------- tcg/optimize.c | 2 +- tcg/tcg-op-gvec.c | 2 +- tcg/tcg-op-ldst.c | 2 +- tcg/tcg-op-vec.c | 2 +- tcg/tcg-op.c | 2 +- tcg/tcg.c | 2 +- tcg/tci.c | 3 +- 9 files changed, 1007 insertions(+), 1008 deletions(-) create mode 100644 include/tcg/tcg-op-common.h diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h new file mode 100644 index 0000000000..04a9ca1fc6 --- /dev/null +++ b/include/tcg/tcg-op-common.h @@ -0,0 +1,996 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Target independent opcode generation functions. + * + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TCG_OP_COMMON_H +#define TCG_TCG_OP_COMMON_H + +#include "tcg/tcg.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +/* Basic output routines. Not for general consumption. */ + +void tcg_gen_op1(TCGOpcode, TCGArg); +void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); +void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); +void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); + +void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); +void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); +void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); + +static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) +{ + tcg_gen_op1(opc, tcgv_i32_arg(a1)); +} + +static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) +{ + tcg_gen_op1(opc, tcgv_i64_arg(a1)); +} + +static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg a1) +{ + tcg_gen_op1(opc, a1); +} + +static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) +{ + tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); +} + +static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) +{ + tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); +} + +static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) +{ + tcg_gen_op2(opc, tcgv_i32_arg(a1), a2); +} + +static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) +{ + tcg_gen_op2(opc, tcgv_i64_arg(a1), a2); +} + +static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) +{ + tcg_gen_op2(opc, a1, a2); +} + +static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGv_i32 a3) +{ + tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); +} + +static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGv_i64 a3) +{ + tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); +} + +static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGArg a3) +{ + tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); +} + +static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGArg a3) +{ + tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); +} + +static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) +{ + tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); +} + +static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) +{ + tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); +} + +static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4)); +} + +static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4)); +} + +static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), a4); +} + +static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4); +} + +static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGArg a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); +} + +static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGArg a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); +} + +static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); +} + +static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); +} + +static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5); +} + +static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5); +} + +static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), a4, a5); +} + +static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4, a5); +} + +static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGv_i32 a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), + tcgv_i32_arg(a6)); +} + +static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGv_i64 a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), + tcgv_i64_arg(a6)); +} + +static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); +} + +static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); +} + +static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGArg a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); +} + +static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGArg a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5, a6); +} + + +/* Generic ops. */ + +static inline void gen_set_label(TCGLabel *l) +{ + l->present = 1; + tcg_gen_op1(INDEX_op_set_label, label_arg(l)); +} + +void tcg_gen_br(TCGLabel *l); +void tcg_gen_mb(TCGBar); + +/** + * tcg_gen_exit_tb() - output exit_tb TCG operation + * @tb: The TranslationBlock from which we are exiting + * @idx: Direct jump slot index, or exit request + * + * See tcg/README for more info about this TCG operation. + * See also tcg.h and the block comment above TB_EXIT_MASK. + * + * For a normal exit from the TB, back to the main loop, @tb should + * be NULL and @idx should be 0. Otherwise, @tb should be valid and + * @idx should be one of the TB_EXIT_ values. + */ +void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx); + +/** + * tcg_gen_goto_tb() - output goto_tb TCG operation + * @idx: Direct jump slot index (0 or 1) + * + * See tcg/README for more info about this TCG operation. + * + * NOTE: In softmmu emulation, direct jumps with goto_tb are only safe within + * the pages this TB resides in because we don't take care of direct jumps when + * address mapping changes, e.g. in tlb_flush(). In user mode, there's only a + * static address translation, so the destination address is always valid, TBs + * are always invalidated properly, and direct jumps are reset when mapping + * changes. + */ +void tcg_gen_goto_tb(unsigned idx); + +/** + * tcg_gen_lookup_and_goto_ptr() - look up the current TB, jump to it if valid + * @addr: Guest address of the target TB + * + * If the TB is not valid, jump to the epilogue. + * + * This operation is optional. If the TCG backend does not implement goto_ptr, + * this op is equivalent to calling tcg_gen_exit_tb() with 0 as the argument. + */ +void tcg_gen_lookup_and_goto_ptr(void); + +static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type, + unsigned wr) +{ + tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); +} + +static inline void tcg_gen_plugin_cb_end(void) +{ + tcg_emit_op(INDEX_op_plugin_cb_end, 0); +} + +/* 32 bit ops */ + +void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg); +void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2); +void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_clzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); +void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); +void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ctpop_i32(TCGv_i32 a1, TCGv_i32 a2); +void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, + unsigned int ofs, unsigned int len); +void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, + unsigned int ofs); +void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *); +void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, int32_t arg2, TCGLabel *); +void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2); +void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, + TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2); +void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags); +void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); + +/* Replicate a value of size @vece from @in to all the lanes in @out */ +void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); + +static inline void tcg_gen_discard_i32(TCGv_i32 arg) +{ + tcg_gen_op1_i32(INDEX_op_discard, arg); +} + +static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (ret != arg) { + tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); + } +} + +static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); +} + +static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); +} + +static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); +} + +static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); +} + +static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_neg_i32) { + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); + } else { + tcg_gen_subfi_i32(ret, 0, arg); + } +} + +static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_not_i32) { + tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); + } else { + tcg_gen_xori_i32(ret, arg, -1); + } +} + +/* 64 bit ops */ + +void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg); +void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2); +void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); +void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); +void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ctpop_i64(TCGv_i64 a1, TCGv_i64 a2); +void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, + unsigned int ofs, unsigned int len); +void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, + unsigned int ofs); +void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *); +void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *); +void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2); +void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, + TCGv_i64 c2, TCGv_i64 v1, TCGv_i64 v2); +void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); +void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); +void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); + +/* Replicate a value of size @vece from @in to all the lanes in @out */ +void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); + +#if TCG_TARGET_REG_BITS == 64 +static inline void tcg_gen_discard_i64(TCGv_i64 arg) +{ + tcg_gen_op1_i64(INDEX_op_discard, arg); +} + +static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (ret != arg) { + tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); + } +} + +static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); +} + +static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); +} +#else /* TCG_TARGET_REG_BITS == 32 */ +void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); + +void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); + +void tcg_gen_discard_i64(TCGv_i64 arg); +void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +#endif /* TCG_TARGET_REG_BITS */ + +static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_HAS_neg_i64) { + tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); + } else { + tcg_gen_subfi_i64(ret, 0, arg); + } +} + +/* Size changing operations. */ + +void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg); +void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg); +void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high); +void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg); +void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg); +void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); +void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); + +void tcg_gen_mov_i128(TCGv_i128 dst, TCGv_i128 src); +void tcg_gen_extr_i128_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i128 arg); +void tcg_gen_concat_i64_i128(TCGv_i128 ret, TCGv_i64 lo, TCGv_i64 hi); + +static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) +{ + tcg_gen_deposit_i64(ret, lo, hi, 32, 32); +} + +/* Local load/store bit ops */ + +void tcg_gen_qemu_ld_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_st_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_ld_i64_chk(TCGv_i64, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_st_i64_chk(TCGv_i64, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_ld_i128_chk(TCGv_i128, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_st_i128_chk(TCGv_i128, TCGTemp *, TCGArg, MemOp, TCGType); + +/* Atomic ops */ + +void tcg_gen_atomic_cmpxchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_cmpxchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_cmpxchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGv_i128, TCGArg, MemOp, TCGType); + +void tcg_gen_nonatomic_cmpxchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_nonatomic_cmpxchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_nonatomic_cmpxchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGv_i128, TCGArg, MemOp, TCGType); + +void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); + +void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_add_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_xor_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_xor_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smin_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smin_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umin_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umin_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smax_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smax_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umax_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umax_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); + +void tcg_gen_atomic_add_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_add_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_and_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_and_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_or_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_or_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xor_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xor_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smin_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smin_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umin_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umin_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); + +/* Vector ops */ + +void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); +void tcg_gen_dup_i32_vec(unsigned vece, TCGv_vec, TCGv_i32); +void tcg_gen_dup_i64_vec(unsigned vece, TCGv_vec, TCGv_i64); +void tcg_gen_dup_mem_vec(unsigned vece, TCGv_vec, TCGv_ptr, tcg_target_long); +void tcg_gen_dupi_vec(unsigned vece, TCGv_vec, uint64_t); +void tcg_gen_add_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_sub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_and_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_or_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_xor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_andc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_abs_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_smin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_umin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_smax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_umax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); + +void tcg_gen_shli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_shri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_sari_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_rotli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_rotri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); + +void tcg_gen_shls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); +void tcg_gen_shrs_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); +void tcg_gen_sars_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); +void tcg_gen_rotls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); + +void tcg_gen_shlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_shrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_sarv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_rotlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_rotrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); + +void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGv_vec r, + TCGv_vec a, TCGv_vec b); + +void tcg_gen_bitsel_vec(unsigned vece, TCGv_vec r, TCGv_vec a, + TCGv_vec b, TCGv_vec c); +void tcg_gen_cmpsel_vec(TCGCond cond, unsigned vece, TCGv_vec r, + TCGv_vec a, TCGv_vec b, TCGv_vec c, TCGv_vec d); + +void tcg_gen_ld_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); +void tcg_gen_st_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); +void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); + +/* Host pointer ops */ + +#if UINTPTR_MAX == UINT32_MAX +# define PTR i32 +# define NAT TCGv_i32 +#else +# define PTR i64 +# define NAT TCGv_i64 +#endif + +static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) +{ + glue(tcg_gen_ld_,PTR)((NAT)r, a, o); +} + +static inline void tcg_gen_st_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) +{ + glue(tcg_gen_st_, PTR)((NAT)r, a, o); +} + +static inline void tcg_gen_discard_ptr(TCGv_ptr a) +{ + glue(tcg_gen_discard_,PTR)((NAT)a); +} + +static inline void tcg_gen_add_ptr(TCGv_ptr r, TCGv_ptr a, TCGv_ptr b) +{ + glue(tcg_gen_add_,PTR)((NAT)r, (NAT)a, (NAT)b); +} + +static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b) +{ + glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b); +} + +static inline void tcg_gen_mov_ptr(TCGv_ptr d, TCGv_ptr s) +{ + glue(tcg_gen_mov_,PTR)((NAT)d, (NAT)s); +} + +static inline void tcg_gen_movi_ptr(TCGv_ptr d, intptr_t s) +{ + glue(tcg_gen_movi_,PTR)((NAT)d, s); +} + +static inline void tcg_gen_brcondi_ptr(TCGCond cond, TCGv_ptr a, + intptr_t b, TCGLabel *label) +{ + glue(tcg_gen_brcondi_,PTR)(cond, (NAT)a, b, label); +} + +static inline void tcg_gen_ext_i32_ptr(TCGv_ptr r, TCGv_i32 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32((NAT)r, a); +#else + tcg_gen_ext_i32_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_trunc_i64_ptr(TCGv_ptr r, TCGv_i64 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extrl_i64_i32((NAT)r, a); +#else + tcg_gen_mov_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_extu_ptr_i64(TCGv_i64 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extu_i32_i64(r, (NAT)a); +#else + tcg_gen_mov_i64(r, (NAT)a); +#endif +} + +static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32(r, (NAT)a); +#else + tcg_gen_extrl_i64_i32(r, (NAT)a); +#endif +} + +#undef PTR +#undef NAT + +#endif /* TCG_TCG_OP_COMMON_H */ diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index b8f0599f3c..47f1dce816 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -1,722 +1,14 @@ +/* SPDX-License-Identifier: MIT */ /* - * Tiny Code Generator for QEMU + * Target dependent opcode generation functions. * * Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. */ #ifndef TCG_TCG_OP_H #define TCG_TCG_OP_H -#include "tcg/tcg.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" - -/* Basic output routines. Not for general consumption. */ - -void tcg_gen_op1(TCGOpcode, TCGArg); -void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); -void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); -void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); -void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); -void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); - -void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); -void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); -void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); - -static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) -{ - tcg_gen_op1(opc, tcgv_i32_arg(a1)); -} - -static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) -{ - tcg_gen_op1(opc, tcgv_i64_arg(a1)); -} - -static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg a1) -{ - tcg_gen_op1(opc, a1); -} - -static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); -} - -static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); -} - -static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), a2); -} - -static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), a2); -} - -static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) -{ - tcg_gen_op2(opc, a1, a2); -} - -static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, - TCGv_i32 a2, TCGv_i32 a3) -{ - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); -} - -static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, - TCGv_i64 a2, TCGv_i64 a3) -{ - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); -} - -static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, - TCGv_i32 a2, TCGArg a3) -{ - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); -} - -static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, - TCGv_i64 a2, TCGArg a3) -{ - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); -} - -static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, - TCGv_ptr base, TCGArg offset) -{ - tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); -} - -static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, - TCGv_ptr base, TCGArg offset) -{ - tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); -} - -static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4)); -} - -static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4)); -} - -static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), a4); -} - -static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), a4); -} - -static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGArg a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); -} - -static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGArg a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); -} - -static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); -} - -static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); -} - -static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5); -} - -static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5); -} - -static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), a4, a5); -} - -static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), a4, a5); -} - -static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGv_i32 a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), - tcgv_i32_arg(a6)); -} - -static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGv_i64 a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), - tcgv_i64_arg(a6)); -} - -static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); -} - -static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); -} - -static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); -} - -static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5, a6); -} - - -/* Generic ops. */ - -static inline void gen_set_label(TCGLabel *l) -{ - l->present = 1; - tcg_gen_op1(INDEX_op_set_label, label_arg(l)); -} - -void tcg_gen_br(TCGLabel *l); -void tcg_gen_mb(TCGBar); - -/* Helper calls. */ - -/* 32 bit ops */ - -void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg); -void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2); -void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_clzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); -void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); -void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ctpop_i32(TCGv_i32 a1, TCGv_i32 a2); -void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, - unsigned int ofs, unsigned int len); -void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, - unsigned int ofs); -void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *); -void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, int32_t arg2, TCGLabel *); -void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 arg1, int32_t arg2); -void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, - TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2); -void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, - TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); -void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, - TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); -void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags); -void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); - -/* Replicate a value of size @vece from @in to all the lanes in @out */ -void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); - -static inline void tcg_gen_discard_i32(TCGv_i32 arg) -{ - tcg_gen_op1_i32(INDEX_op_discard, arg); -} - -static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (ret != arg) { - tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); - } -} - -static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_neg_i32) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); - } else { - tcg_gen_subfi_i32(ret, 0, arg); - } -} - -static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_not_i32) { - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); - } else { - tcg_gen_xori_i32(ret, arg, -1); - } -} - -/* 64 bit ops */ - -void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg); -void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2); -void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); -void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); -void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ctpop_i64(TCGv_i64 a1, TCGv_i64 a2); -void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, - unsigned int ofs, unsigned int len); -void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, - unsigned int ofs); -void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *); -void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *); -void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, int64_t arg2); -void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, - TCGv_i64 c2, TCGv_i64 v1, TCGv_i64 v2); -void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, - TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); -void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, - TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); -void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); -void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); -void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); - -/* Replicate a value of size @vece from @in to all the lanes in @out */ -void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); - -#if TCG_TARGET_REG_BITS == 64 -static inline void tcg_gen_discard_i64(TCGv_i64 arg) -{ - tcg_gen_op1_i64(INDEX_op_discard, arg); -} - -static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (ret != arg) { - tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); - } -} - -static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); -} -#else /* TCG_TARGET_REG_BITS == 32 */ -void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); - -void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); - -void tcg_gen_discard_i64(TCGv_i64 arg); -void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -#endif /* TCG_TARGET_REG_BITS */ - -static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_neg_i64) { - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); - } else { - tcg_gen_subfi_i64(ret, 0, arg); - } -} - -/* Size changing operations. */ - -void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg); -void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg); -void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high); -void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg); -void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg); -void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); -void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); - -void tcg_gen_mov_i128(TCGv_i128 dst, TCGv_i128 src); -void tcg_gen_extr_i128_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i128 arg); -void tcg_gen_concat_i64_i128(TCGv_i128 ret, TCGv_i64 lo, TCGv_i64 hi); - -static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) -{ - tcg_gen_deposit_i64(ret, lo, hi, 32, 32); -} - -/* QEMU specific operations. */ +#include "tcg/tcg-op-common.h" #ifndef TARGET_LONG_BITS #error must include QEMU headers @@ -756,57 +48,6 @@ static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, # error "Unhandled number of operands to insn_start" #endif -/** - * tcg_gen_exit_tb() - output exit_tb TCG operation - * @tb: The TranslationBlock from which we are exiting - * @idx: Direct jump slot index, or exit request - * - * See tcg/README for more info about this TCG operation. - * See also tcg.h and the block comment above TB_EXIT_MASK. - * - * For a normal exit from the TB, back to the main loop, @tb should - * be NULL and @idx should be 0. Otherwise, @tb should be valid and - * @idx should be one of the TB_EXIT_ values. - */ -void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx); - -/** - * tcg_gen_goto_tb() - output goto_tb TCG operation - * @idx: Direct jump slot index (0 or 1) - * - * See tcg/README for more info about this TCG operation. - * - * NOTE: In softmmu emulation, direct jumps with goto_tb are only safe within - * the pages this TB resides in because we don't take care of direct jumps when - * address mapping changes, e.g. in tlb_flush(). In user mode, there's only a - * static address translation, so the destination address is always valid, TBs - * are always invalidated properly, and direct jumps are reset when mapping - * changes. - */ -void tcg_gen_goto_tb(unsigned idx); - -/** - * tcg_gen_lookup_and_goto_ptr() - look up the current TB, jump to it if valid - * @addr: Guest address of the target TB - * - * If the TB is not valid, jump to the epilogue. - * - * This operation is optional. If the TCG backend does not implement goto_ptr, - * this op is equivalent to calling tcg_gen_exit_tb() with 0 as the argument. - */ -void tcg_gen_lookup_and_goto_ptr(void); - -static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type, - unsigned wr) -{ - tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); -} - -static inline void tcg_gen_plugin_cb_end(void) -{ - tcg_emit_op(INDEX_op_plugin_cb_end, 0); -} - #if TARGET_LONG_BITS == 32 typedef TCGv_i32 TCGv; #define tcg_temp_new() tcg_temp_new_i32() @@ -827,13 +68,6 @@ typedef TCGv_i64 TCGv; #error Unhandled TARGET_LONG_BITS value #endif -void tcg_gen_qemu_ld_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); -void tcg_gen_qemu_st_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); -void tcg_gen_qemu_ld_i64_chk(TCGv_i64, TCGTemp *, TCGArg, MemOp, TCGType); -void tcg_gen_qemu_st_i64_chk(TCGv_i64, TCGTemp *, TCGArg, MemOp, TCGType); -void tcg_gen_qemu_ld_i128_chk(TCGv_i128, TCGTemp *, TCGArg, MemOp, TCGType); -void tcg_gen_qemu_st_i128_chk(TCGv_i128, TCGTemp *, TCGArg, MemOp, TCGType); - static inline void tcg_gen_qemu_ld_i32(TCGv_i32 v, TCGv a, TCGArg i, MemOp m) { @@ -870,91 +104,6 @@ tcg_gen_qemu_st_i128(TCGv_i128 v, TCGv a, TCGArg i, MemOp m) tcg_gen_qemu_st_i128_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -void tcg_gen_atomic_cmpxchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_cmpxchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_cmpxchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, - TCGv_i128, TCGArg, MemOp, TCGType); - -void tcg_gen_nonatomic_cmpxchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_nonatomic_cmpxchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_nonatomic_cmpxchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, - TCGv_i128, TCGArg, MemOp, TCGType); - -void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); - -void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_add_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_and_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_and_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_or_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_or_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_xor_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_xor_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_smin_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_smin_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_umin_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_umin_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_smax_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_smax_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_umax_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_fetch_umax_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); - -void tcg_gen_atomic_add_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_add_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_and_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_and_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_or_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_or_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_xor_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_xor_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_smin_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_smin_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_umin_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_umin_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_smax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_smax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, - TCGArg, MemOp, TCGType); -void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, - TCGArg, MemOp, TCGType); - #define DEF_ATOMIC2(N, S) \ static inline void N##_##S(TCGv_##S r, TCGv a, TCGv_##S v, \ TCGArg i, MemOp m) \ @@ -1013,63 +162,6 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #undef DEF_ATOMIC2 #undef DEF_ATOMIC3 -void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); -void tcg_gen_dup_i32_vec(unsigned vece, TCGv_vec, TCGv_i32); -void tcg_gen_dup_i64_vec(unsigned vece, TCGv_vec, TCGv_i64); -void tcg_gen_dup_mem_vec(unsigned vece, TCGv_vec, TCGv_ptr, tcg_target_long); -void tcg_gen_dupi_vec(unsigned vece, TCGv_vec, uint64_t); -void tcg_gen_add_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_sub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_and_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_or_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_xor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_andc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a); -void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a); -void tcg_gen_abs_vec(unsigned vece, TCGv_vec r, TCGv_vec a); -void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_smin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_umin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_smax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_umax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); - -void tcg_gen_shli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_shri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_sari_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_rotli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_rotri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); - -void tcg_gen_shls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); -void tcg_gen_shrs_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); -void tcg_gen_sars_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); -void tcg_gen_rotls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); - -void tcg_gen_shlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_shrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_sarv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_rotlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_rotrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); - -void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGv_vec r, - TCGv_vec a, TCGv_vec b); - -void tcg_gen_bitsel_vec(unsigned vece, TCGv_vec r, TCGv_vec a, - TCGv_vec b, TCGv_vec c); -void tcg_gen_cmpsel_vec(TCGCond cond, unsigned vece, TCGv_vec r, - TCGv_vec a, TCGv_vec b, TCGv_vec c, TCGv_vec d); - -void tcg_gen_ld_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); -void tcg_gen_st_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); -void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); - #if TARGET_LONG_BITS == 64 #define tcg_gen_movi_tl tcg_gen_movi_i64 #define tcg_gen_mov_tl tcg_gen_mov_i64 @@ -1309,94 +401,6 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); : (VECE) == MO_32 ? 0x00000001ul * (uint32_t)(C) \ : (qemu_build_not_reached_always(), 0)) \ : (target_long)dup_const(VECE, C)) -#endif - -#if UINTPTR_MAX == UINT32_MAX -# define PTR i32 -# define NAT TCGv_i32 -#else -# define PTR i64 -# define NAT TCGv_i64 -#endif - -static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) -{ - glue(tcg_gen_ld_,PTR)((NAT)r, a, o); -} - -static inline void tcg_gen_st_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) -{ - glue(tcg_gen_st_, PTR)((NAT)r, a, o); -} - -static inline void tcg_gen_discard_ptr(TCGv_ptr a) -{ - glue(tcg_gen_discard_,PTR)((NAT)a); -} - -static inline void tcg_gen_add_ptr(TCGv_ptr r, TCGv_ptr a, TCGv_ptr b) -{ - glue(tcg_gen_add_,PTR)((NAT)r, (NAT)a, (NAT)b); -} - -static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b) -{ - glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b); -} - -static inline void tcg_gen_mov_ptr(TCGv_ptr d, TCGv_ptr s) -{ - glue(tcg_gen_mov_,PTR)((NAT)d, (NAT)s); -} - -static inline void tcg_gen_movi_ptr(TCGv_ptr d, intptr_t s) -{ - glue(tcg_gen_movi_,PTR)((NAT)d, s); -} - -static inline void tcg_gen_brcondi_ptr(TCGCond cond, TCGv_ptr a, - intptr_t b, TCGLabel *label) -{ - glue(tcg_gen_brcondi_,PTR)(cond, (NAT)a, b, label); -} - -static inline void tcg_gen_ext_i32_ptr(TCGv_ptr r, TCGv_i32 a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_mov_i32((NAT)r, a); -#else - tcg_gen_ext_i32_i64((NAT)r, a); -#endif -} - -static inline void tcg_gen_trunc_i64_ptr(TCGv_ptr r, TCGv_i64 a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_extrl_i64_i32((NAT)r, a); -#else - tcg_gen_mov_i64((NAT)r, a); -#endif -} - -static inline void tcg_gen_extu_ptr_i64(TCGv_i64 r, TCGv_ptr a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_extu_i32_i64(r, (NAT)a); -#else - tcg_gen_mov_i64(r, (NAT)a); -#endif -} - -static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_mov_i32(r, (NAT)a); -#else - tcg_gen_extrl_i64_i32(r, (NAT)a); -#endif -} - -#undef PTR -#undef NAT +#endif /* TARGET_LONG_BITS == 64 */ #endif /* TCG_TCG_OP_H */ diff --git a/tcg/optimize.c b/tcg/optimize.c index bf975a3a6c..d2156367a3 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qemu/int128.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "tcg-internal.h" #define CASE_OP_32_64(x) \ diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index f51bcaa87b..7a9599e49e 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "tcg/tcg-op-gvec.h" #include "tcg/tcg-gvec-desc.h" diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 3d27c347d7..3c00bf0c95 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -26,7 +26,7 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index aeeb2435cb..35d67eeda0 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" #include "tcg-internal.h" diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index edbd1c61d7..8c1ad49c4e 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -26,7 +26,7 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" diff --git a/tcg/tcg.c b/tcg/tcg.c index 2d17c09aea..e13d0a6f09 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -42,7 +42,7 @@ #include "exec/exec-all.h" #include "exec/tlb-common.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #if UINTPTR_MAX == UINT32_MAX # define ELF_CLASS ELFCLASS32 diff --git a/tcg/tci.c b/tcg/tci.c index bab4397bc5..813572ff39 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -18,8 +18,7 @@ */ #include "qemu/osdep.h" -#include "exec/cpu_ldst.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg.h" #include "tcg/tcg-ldst.h" #include From a46f42d96f3b1f548f3b5545c6da9641b744917d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Mar 2023 11:41:03 -0700 Subject: [PATCH 086/412] target/arm: Include helper-gen.h in translator.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been included via tcg-op-common.h via tcg-op.h, but that is going away. It is needed for inlines within translator.h, so we might as well do it there and not individually in each translator c file. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 2 -- target/arm/tcg/translate-sme.c | 1 - target/arm/tcg/translate-sve.c | 2 -- target/arm/tcg/translate.c | 2 -- target/arm/tcg/translate.h | 1 + 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 741a608739..bc0cb98955 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -29,8 +29,6 @@ #include "qemu/host-utils.h" #include "semihosting/semihost.h" #include "exec/gen-icount.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" #include "exec/log.h" #include "cpregs.h" #include "translate-a64.h" diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index e3adba314e..b0812d9dd6 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -23,7 +23,6 @@ #include "tcg/tcg-op-gvec.h" #include "tcg/tcg-gvec-desc.h" #include "translate.h" -#include "exec/helper-gen.h" #include "translate-a64.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 92ab290106..106baf311f 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -27,8 +27,6 @@ #include "arm_ldst.h" #include "translate.h" #include "internals.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" #include "exec/log.h" #include "translate-a64.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 7468476724..c89825ad6a 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -30,8 +30,6 @@ #include "qemu/bitops.h" #include "arm_ldst.h" #include "semihosting/semihost.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" #include "exec/log.h" #include "cpregs.h" diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index a9d1f4adc2..868a3abd0d 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -2,6 +2,7 @@ #define TARGET_ARM_TRANSLATE_H #include "exec/translator.h" +#include "exec/helper-gen.h" #include "internals.h" From 13e27d1fa4b0b2109f91bcd078e93cb6a6d7a7cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Mar 2023 11:44:06 -0700 Subject: [PATCH 087/412] target/hexagon: Include helper-gen.h where needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been included via tcg-op-common.h via tcg-op.h, but that is going away. In idef-parser.y, shuffle some tcg related includes into a more logical order. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hexagon/genptr.c | 1 + target/hexagon/idef-parser/idef-parser.y | 3 ++- target/hexagon/translate.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index bcb287dd8b..217bc7bb5a 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -20,6 +20,7 @@ #include "internal.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" +#include "exec/helper-gen.h" #include "insn.h" #include "opcodes.h" #include "translate.h" diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index 5c983954ed..cd2612eb8c 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y @@ -843,13 +843,14 @@ int main(int argc, char **argv) fputs("#include \"qemu/log.h\"\n", output_file); fputs("#include \"cpu.h\"\n", output_file); fputs("#include \"internal.h\"\n", output_file); + fputs("#include \"tcg/tcg.h\"\n", output_file); fputs("#include \"tcg/tcg-op.h\"\n", output_file); + fputs("#include \"exec/helper-gen.h\"\n", output_file); fputs("#include \"insn.h\"\n", output_file); fputs("#include \"opcodes.h\"\n", output_file); fputs("#include \"translate.h\"\n", output_file); fputs("#define QEMU_GENERATE\n", output_file); fputs("#include \"genptr.h\"\n", output_file); - fputs("#include \"tcg/tcg.h\"\n", output_file); fputs("#include \"macros.h\"\n", output_file); fprintf(output_file, "#include \"%s\"\n", argv[ARG_INDEX_EMITTER_H]); diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 8838ab2364..42a7697fc9 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" +#include "exec/helper-gen.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "internal.h" From 8da7b594354eb66ad6e63b094d31e1d146a1558d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Mar 2023 11:55:33 -0700 Subject: [PATCH 088/412] tcg: Remove outdated comments in helper-head.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/helper-head.h | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index f863a6ef5d..a355ef8ebe 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -1,18 +1,6 @@ -/* Helper file for declaring TCG helper functions. - Used by other helper files. - - Targets should use DEF_HELPER_N and DEF_HELPER_FLAGS_N to declare helper - functions. Names should be specified without the helper_ prefix, and - the return and argument types specified. 3 basic types are understood - (i32, i64 and ptr). Additional aliases are provided for convenience and - to match the types used by the C helper implementation. - - The target helper.h should be included in all files that use/define - helper functions. THis will ensure that function prototypes are - consistent. In addition it should be included an extra two times for - helper.c, defining: - GEN_HELPER 1 to produce op generation functions (gen_helper_*) - GEN_HELPER 2 to do runtime registration helper functions. +/* + * Helper file for declaring TCG helper functions. + * Used by other helper files. */ #ifndef EXEC_HELPER_HEAD_H From 0f4e14c25a1900ee77ce29060adbaede1367bd3d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Mar 2023 18:09:28 -0700 Subject: [PATCH 089/412] tcg: Move TCGHelperInfo and dependencies to tcg/helper-info.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be required outside of tcg-internal.h soon. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/helper-info.h | 59 +++++++++++++++++++++++++++++++++++++++ tcg/tcg-internal.h | 47 +------------------------------ 2 files changed, 60 insertions(+), 46 deletions(-) create mode 100644 include/tcg/helper-info.h diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h new file mode 100644 index 0000000000..f65f81c2e7 --- /dev/null +++ b/include/tcg/helper-info.h @@ -0,0 +1,59 @@ +/* + * TCG Helper Infomation Structure + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TCG_HELPER_INFO_H +#define TCG_HELPER_INFO_H + +#ifdef CONFIG_TCG_INTERPRETER +#include +#endif + +/* + * Describe the calling convention of a given argument type. + */ +typedef enum { + TCG_CALL_RET_NORMAL, /* by registers */ + TCG_CALL_RET_BY_REF, /* for i128, by reference */ + TCG_CALL_RET_BY_VEC, /* for i128, by vector register */ +} TCGCallReturnKind; + +typedef enum { + TCG_CALL_ARG_NORMAL, /* by registers (continuing onto stack) */ + TCG_CALL_ARG_EVEN, /* like normal, but skipping odd slots */ + TCG_CALL_ARG_EXTEND, /* for i32, as a sign/zero-extended i64 */ + TCG_CALL_ARG_EXTEND_U, /* ... as a zero-extended i64 */ + TCG_CALL_ARG_EXTEND_S, /* ... as a sign-extended i64 */ + TCG_CALL_ARG_BY_REF, /* for i128, by reference, first */ + TCG_CALL_ARG_BY_REF_N, /* ... by reference, subsequent */ +} TCGCallArgumentKind; + +typedef struct TCGCallArgumentLoc { + TCGCallArgumentKind kind : 8; + unsigned arg_slot : 8; + unsigned ref_slot : 8; + unsigned arg_idx : 4; + unsigned tmp_subindex : 2; +} TCGCallArgumentLoc; + +typedef struct TCGHelperInfo { + void *func; + const char *name; +#ifdef CONFIG_TCG_INTERPRETER + ffi_cif *cif; +#endif + unsigned typemask : 32; + unsigned flags : 8; + unsigned nr_in : 8; + unsigned nr_out : 8; + TCGCallReturnKind out_kind : 8; + + /* Maximum physical arguments are constrained by TCG_TYPE_I128. */ + TCGCallArgumentLoc in[MAX_CALL_IARGS * (128 / TCG_TARGET_REG_BITS)]; +} TCGHelperInfo; + +#endif /* TCG_HELPER_INFO_H */ diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index 67b698bd5c..fbe62b31b8 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -25,55 +25,10 @@ #ifndef TCG_INTERNAL_H #define TCG_INTERNAL_H -#ifdef CONFIG_TCG_INTERPRETER -#include -#endif +#include "tcg/helper-info.h" #define TCG_HIGHWATER 1024 -/* - * Describe the calling convention of a given argument type. - */ -typedef enum { - TCG_CALL_RET_NORMAL, /* by registers */ - TCG_CALL_RET_BY_REF, /* for i128, by reference */ - TCG_CALL_RET_BY_VEC, /* for i128, by vector register */ -} TCGCallReturnKind; - -typedef enum { - TCG_CALL_ARG_NORMAL, /* by registers (continuing onto stack) */ - TCG_CALL_ARG_EVEN, /* like normal, but skipping odd slots */ - TCG_CALL_ARG_EXTEND, /* for i32, as a sign/zero-extended i64 */ - TCG_CALL_ARG_EXTEND_U, /* ... as a zero-extended i64 */ - TCG_CALL_ARG_EXTEND_S, /* ... as a sign-extended i64 */ - TCG_CALL_ARG_BY_REF, /* for i128, by reference, first */ - TCG_CALL_ARG_BY_REF_N, /* ... by reference, subsequent */ -} TCGCallArgumentKind; - -typedef struct TCGCallArgumentLoc { - TCGCallArgumentKind kind : 8; - unsigned arg_slot : 8; - unsigned ref_slot : 8; - unsigned arg_idx : 4; - unsigned tmp_subindex : 2; -} TCGCallArgumentLoc; - -typedef struct TCGHelperInfo { - void *func; - const char *name; -#ifdef CONFIG_TCG_INTERPRETER - ffi_cif *cif; -#endif - unsigned typemask : 32; - unsigned flags : 8; - unsigned nr_in : 8; - unsigned nr_out : 8; - TCGCallReturnKind out_kind : 8; - - /* Maximum physical arguments are constrained by TCG_TYPE_I128. */ - TCGCallArgumentLoc in[MAX_CALL_IARGS * (128 / TCG_TARGET_REG_BITS)]; -} TCGHelperInfo; - extern TCGContext tcg_init_ctx; extern TCGContext **tcg_ctxs; extern unsigned int tcg_cur_ctxs; From d53106c997e5c8e61e37ae9ff9f0e1f243b03968 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Mar 2023 10:37:04 -0700 Subject: [PATCH 090/412] tcg: Pass TCGHelperInfo to tcg_gen_callN In preparation for compiling tcg/ only once, eliminate the all_helpers array. Instantiate the info structs for the generic helpers in accel/tcg/, and the structs for the target-specific helpers in each translate.c. Since we don't see all of the info structs at startup, initialize at first use, using g_once_init_* to make sure we don't race while doing so. Reviewed-by: Anton Johansson Signed-off-by: Richard Henderson --- MAINTAINERS | 1 + accel/tcg/plugin-gen.c | 5 ++ accel/tcg/tcg-runtime.c | 4 ++ include/exec/helper-gen.h | 66 ++++++++++++-------- include/exec/helper-info.c.inc | 96 +++++++++++++++++++++++++++++ include/exec/helper-tcg.h | 75 ----------------------- include/qemu/typedefs.h | 1 + include/tcg/helper-info.h | 9 ++- include/tcg/tcg.h | 2 +- target/alpha/translate.c | 3 + target/arm/tcg/translate.c | 3 + target/avr/translate.c | 5 ++ target/cris/translate.c | 6 +- target/hexagon/translate.c | 4 ++ target/hppa/translate.c | 5 ++ target/i386/tcg/translate.c | 5 ++ target/loongarch/translate.c | 4 ++ target/m68k/translate.c | 3 + target/microblaze/translate.c | 4 ++ target/mips/tcg/translate.c | 5 ++ target/nios2/translate.c | 5 ++ target/openrisc/translate.c | 5 ++ target/ppc/translate.c | 4 ++ target/riscv/translate.c | 4 ++ target/rx/translate.c | 5 ++ target/s390x/tcg/translate.c | 4 ++ target/sh4/translate.c | 4 ++ target/sparc/translate.c | 3 + target/tricore/translate.c | 5 ++ target/xtensa/translate.c | 4 ++ tcg/tcg.c | 108 ++++++++++++--------------------- 31 files changed, 282 insertions(+), 175 deletions(-) create mode 100644 include/exec/helper-info.c.inc delete mode 100644 include/exec/helper-tcg.h diff --git a/MAINTAINERS b/MAINTAINERS index 89f274f85e..a1b8376f4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -154,6 +154,7 @@ F: include/exec/exec-all.h F: include/exec/tb-flush.h F: include/exec/target_long.h F: include/exec/helper*.h +F: include/exec/helper-info.c.inc F: include/sysemu/cpus.h F: include/sysemu/tcg.h F: include/hw/core/tcg-cpu-ops.h diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 5b73a39ce5..40b34a0403 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -49,6 +49,11 @@ #include "exec/exec-all.h" #include "exec/plugin-gen.h" #include "exec/translator.h" +#include "exec/helper-proto.h" + +#define HELPER_H "accel/tcg/plugin-helpers.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #ifdef CONFIG_SOFTMMU # define CONFIG_SOFTMMU_GATE 1 diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index e4e030043f..14b59a36e5 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -31,6 +31,10 @@ #include "exec/log.h" #include "tcg/tcg.h" +#define HELPER_H "accel/tcg/tcg-runtime.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* 32-bit helpers */ int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2) diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h index 7b6ca975ef..248cff3351 100644 --- a/include/exec/helper-gen.h +++ b/include/exec/helper-gen.h @@ -1,81 +1,96 @@ -/* Helper file for declaring TCG helper functions. - This one expands generation functions for tcg opcodes. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands generation functions for tcg opcodes. + * Define HELPER_H for the header file to be expanded, + * and static inline to change from global file scope. + */ #ifndef HELPER_GEN_H #define HELPER_GEN_H +#include "tcg/tcg.h" +#include "tcg/helper-info.h" #include "exec/helper-head.h" #define DEF_HELPER_FLAGS_0(name, flags, ret) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ { \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 0, NULL); \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 0, NULL); \ } #define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1)) \ { \ - TCGTemp *args[1] = { dh_arg(t1, 1) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 1, args); \ + TCGTemp *args[1] = { dh_arg(t1, 1) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 1, args); \ } #define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ { \ - TCGTemp *args[2] = { dh_arg(t1, 1), dh_arg(t2, 2) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 2, args); \ + TCGTemp *args[2] = { dh_arg(t1, 1), dh_arg(t2, 2) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 2, args); \ } #define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ { \ - TCGTemp *args[3] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 3, args); \ + TCGTemp *args[3] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 3, args); \ } #define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ - TCGTemp *args[4] = { dh_arg(t1, 1), dh_arg(t2, 2), \ - dh_arg(t3, 3), dh_arg(t4, 4) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 4, args); \ + TCGTemp *args[4] = { dh_arg(t1, 1), dh_arg(t2, 2), \ + dh_arg(t3, 3), dh_arg(t4, 4) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 4, args); \ } #define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \ { \ - TCGTemp *args[5] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 5, args); \ + TCGTemp *args[5] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 5, args); \ } #define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \ { \ - TCGTemp *args[6] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 6, args); \ + TCGTemp *args[6] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 6, args); \ } #define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7)\ +extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ dh_arg_decl(t7, 7)) \ { \ - TCGTemp *args[7] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ - dh_arg(t7, 7) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 7, args); \ + TCGTemp *args[7] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ + dh_arg(t7, 7) }; \ + tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 7, args); \ } #include "helper.h" @@ -90,6 +105,5 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ #undef DEF_HELPER_FLAGS_5 #undef DEF_HELPER_FLAGS_6 #undef DEF_HELPER_FLAGS_7 -#undef GEN_HELPER #endif /* HELPER_GEN_H */ diff --git a/include/exec/helper-info.c.inc b/include/exec/helper-info.c.inc new file mode 100644 index 0000000000..530d2e6d35 --- /dev/null +++ b/include/exec/helper-info.c.inc @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands info structures for tcg helpers. + * Define HELPER_H for the header file to be expanded. + */ + +#include "tcg/tcg.h" +#include "tcg/helper-info.h" +#include "exec/helper-head.h" + +/* + * Need one more level of indirection before stringification + * to get all the macros expanded first. + */ +#define str(s) #s + +#define DEF_HELPER_FLAGS_0(NAME, FLAGS, RET) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) \ + }; + +#define DEF_HELPER_FLAGS_1(NAME, FLAGS, RET, T1) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + }; + +#define DEF_HELPER_FLAGS_2(NAME, FLAGS, RET, T1, T2) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) \ + }; + +#define DEF_HELPER_FLAGS_3(NAME, FLAGS, RET, T1, T2, T3) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + }; + +#define DEF_HELPER_FLAGS_4(NAME, FLAGS, RET, T1, T2, T3, T4) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) \ + }; + +#define DEF_HELPER_FLAGS_5(NAME, FLAGS, RET, T1, T2, T3, T4, T5) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) | dh_typemask(T5, 5) \ + }; + +#define DEF_HELPER_FLAGS_6(NAME, FLAGS, RET, T1, T2, T3, T4, T5, T6) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) | dh_typemask(T5, 5) \ + | dh_typemask(T6, 6) \ + }; + +#define DEF_HELPER_FLAGS_7(NAME, FLAGS, RET, T1, T2, T3, T4, T5, T6, T7) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) | dh_typemask(T5, 5) \ + | dh_typemask(T6, 6) | dh_typemask(T7, 7) \ + }; + +#include HELPER_H + +#undef str +#undef DEF_HELPER_FLAGS_0 +#undef DEF_HELPER_FLAGS_1 +#undef DEF_HELPER_FLAGS_2 +#undef DEF_HELPER_FLAGS_3 +#undef DEF_HELPER_FLAGS_4 +#undef DEF_HELPER_FLAGS_5 +#undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h deleted file mode 100644 index 3933258f1a..0000000000 --- a/include/exec/helper-tcg.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Helper file for declaring TCG helper functions. - This one defines data structures private to tcg.c. */ - -#ifndef HELPER_TCG_H -#define HELPER_TCG_H - -#include "exec/helper-head.h" - -/* Need one more level of indirection before stringification - to get all the macros expanded first. */ -#define str(s) #s - -#define DEF_HELPER_FLAGS_0(NAME, FLAGS, ret) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) }, - -#define DEF_HELPER_FLAGS_1(NAME, FLAGS, ret, t1) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) }, - -#define DEF_HELPER_FLAGS_2(NAME, FLAGS, ret, t1, t2) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) }, - -#define DEF_HELPER_FLAGS_3(NAME, FLAGS, ret, t1, t2, t3) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) }, - -#define DEF_HELPER_FLAGS_4(NAME, FLAGS, ret, t1, t2, t3, t4) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) }, - -#define DEF_HELPER_FLAGS_5(NAME, FLAGS, ret, t1, t2, t3, t4, t5) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) \ - | dh_typemask(t5, 5) }, - -#define DEF_HELPER_FLAGS_6(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) \ - | dh_typemask(t5, 5) | dh_typemask(t6, 6) }, - -#define DEF_HELPER_FLAGS_7(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6, t7) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) \ - | dh_typemask(t5, 5) | dh_typemask(t6, 6) | dh_typemask(t7, 7) }, - -#include "helper.h" -#include "accel/tcg/tcg-runtime.h" -#include "accel/tcg/plugin-helpers.h" - -#undef str -#undef DEF_HELPER_FLAGS_0 -#undef DEF_HELPER_FLAGS_1 -#undef DEF_HELPER_FLAGS_2 -#undef DEF_HELPER_FLAGS_3 -#undef DEF_HELPER_FLAGS_4 -#undef DEF_HELPER_FLAGS_5 -#undef DEF_HELPER_FLAGS_6 -#undef DEF_HELPER_FLAGS_7 - -#endif /* HELPER_TCG_H */ diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 8e9ef252f5..8c1840bfc1 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -131,6 +131,7 @@ typedef struct ReservedRegion ReservedRegion; typedef struct SavedIOTLB SavedIOTLB; typedef struct SHPCDevice SHPCDevice; typedef struct SSIBus SSIBus; +typedef struct TCGHelperInfo TCGHelperInfo; typedef struct TranslationBlock TranslationBlock; typedef struct VirtIODevice VirtIODevice; typedef struct Visitor Visitor; diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h index f65f81c2e7..4b6c9b43e8 100644 --- a/include/tcg/helper-info.h +++ b/include/tcg/helper-info.h @@ -40,12 +40,17 @@ typedef struct TCGCallArgumentLoc { unsigned tmp_subindex : 2; } TCGCallArgumentLoc; -typedef struct TCGHelperInfo { +struct TCGHelperInfo { void *func; const char *name; + + /* Used with g_once_init_enter. */ #ifdef CONFIG_TCG_INTERPRETER ffi_cif *cif; +#else + uintptr_t init; #endif + unsigned typemask : 32; unsigned flags : 8; unsigned nr_in : 8; @@ -54,6 +59,6 @@ typedef struct TCGHelperInfo { /* Maximum physical arguments are constrained by TCG_TYPE_I128. */ TCGCallArgumentLoc in[MAX_CALL_IARGS * (128 / TCG_TARGET_REG_BITS)]; -} TCGHelperInfo; +}; #endif /* TCG_HELPER_INFO_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 9b2833b31d..34035dab81 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -937,7 +937,7 @@ typedef struct TCGTargetOpDef { bool tcg_op_supported(TCGOpcode op); -void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args); +void tcg_gen_callN(TCGHelperInfo *, TCGTemp *ret, int nargs, TCGTemp **args); TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs); void tcg_op_remove(TCGContext *s, TCGOp *op); diff --git a/target/alpha/translate.c b/target/alpha/translate.c index be8adb2526..545e5743c3 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -30,6 +30,9 @@ #include "exec/translator.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #undef ALPHA_DEBUG_DISAS #define CONFIG_SOFTFLOAT_INLINE diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index c89825ad6a..4d84850d74 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -33,6 +33,9 @@ #include "exec/log.h" #include "cpregs.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) #define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) diff --git a/target/avr/translate.c b/target/avr/translate.c index cd82f5d591..4fa40b568a 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -31,6 +31,11 @@ #include "exec/translator.h" #include "exec/gen-icount.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* * Define if you want a BREAK instruction translated to a breakpoint * Active debugging connection is assumed diff --git a/target/cris/translate.c b/target/cris/translate.c index b2beb9964d..3c21826cc2 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -34,11 +34,13 @@ #include "exec/translator.h" #include "crisv32-decode.h" #include "qemu/qemu-print.h" - #include "exec/helper-gen.h" - #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define DISAS_CRIS 0 #if DISAS_CRIS diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 42a7697fc9..00e25035ce 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -31,6 +31,10 @@ #include "genptr.h" #include "printinsn.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #include "analyze_funcs_generated.c.inc" typedef void (*AnalyzeInsn)(DisasContext *ctx); diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 59e4688bfa..2c50fa72c3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -29,6 +29,11 @@ #include "exec/translator.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* Since we have a distinction between register size and address size, we need to redefine all of these. */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 91c9c0c478..d509105505 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -34,6 +34,11 @@ #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + #define PREFIX_REPZ 0x01 #define PREFIX_REPNZ 0x02 #define PREFIX_LOCK 0x04 diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c index ae53f5ee9d..67140ada56 100644 --- a/target/loongarch/translate.c +++ b/target/loongarch/translate.c @@ -26,6 +26,10 @@ static TCGv cpu_lladdr, cpu_llval; #include "exec/gen-icount.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define DISAS_STOP DISAS_TARGET_0 #define DISAS_EXIT DISAS_TARGET_1 #define DISAS_EXIT_UPDATE DISAS_TARGET_2 diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 44d852b106..90ca51fb9e 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -34,6 +34,9 @@ #include "exec/log.h" #include "fpu/softfloat.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H //#define DEBUG_DISPATCH 1 diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index ee0d7b81ad..7a5d1066da 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -31,6 +31,10 @@ #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define EXTRACT_FIELD(src, start, end) \ (((src) >> start) & ((1 << (end - start + 1)) - 1)) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index a6ca2e5a3b..bff1859b86 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -37,6 +37,11 @@ #include "fpu_helper.h" #include "translate.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* * Many sysemu-only helpers are not reachable for user-only. * Define stub generators here, so that we need not either sprinkle diff --git a/target/nios2/translate.c b/target/nios2/translate.c index a548e16ed5..28c1d700e1 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -35,6 +35,11 @@ #include "exec/gen-icount.h" #include "semihosting/semihost.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* is_jmp field values */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 43ba0cc1ad..06e6eae952 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -35,6 +35,11 @@ #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* is_jmp field values */ #define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */ #define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9b7884586c..67d7ee0a70 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -41,6 +41,10 @@ #include "qemu/qemu-print.h" #include "qapi/error.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define CPU_SINGLE_STEP 0x1 #define CPU_BRANCH_STEP 0x2 diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 928da0d3f0..ed968162da 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -33,6 +33,10 @@ #include "instmap.h" #include "internals.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* global register indices */ static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ diff --git a/target/rx/translate.c b/target/rx/translate.c index 70fad98e93..89dbec26f9 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -28,6 +28,11 @@ #include "exec/translator.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + typedef struct DisasContext { DisasContextBase base; CPURXState *env; diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 3eb3708d55..60b17585a7 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -46,6 +46,10 @@ #include "exec/log.h" #include "qemu/atomic128.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* Information that (most) every instruction needs to manipulate. */ typedef struct DisasContext DisasContext; diff --git a/target/sh4/translate.c b/target/sh4/translate.c index d9accfa1e7..9d2c7a3337 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -29,6 +29,10 @@ #include "exec/log.h" #include "qemu/qemu-print.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + typedef struct DisasContext { DisasContextBase base; diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 9377798490..ebaf376500 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -33,6 +33,9 @@ #include "exec/log.h" #include "asi.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #define DYNAMIC_PC 1 /* dynamic pc value */ #define JUMP_PC 2 /* dynamic pc value which takes only two values diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 2646cb3eb5..eee935bbaf 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -33,6 +33,11 @@ #include "exec/translator.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* * TCG registers */ diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 728aeebebf..11bb8c079b 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -45,6 +45,10 @@ #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + struct DisasContext { DisasContextBase base; diff --git a/tcg/tcg.c b/tcg/tcg.c index e13d0a6f09..ffd3ccaff7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -848,13 +848,6 @@ void tcg_pool_reset(TCGContext *s) s->pool_current = NULL; } -#include "exec/helper-proto.h" - -static TCGHelperInfo all_helpers[] = { -#include "exec/helper-tcg.h" -}; -static GHashTable *helper_table; - /* * Create TCGHelperInfo structures for "tcg/tcg-ldst.h" functions, * akin to what "exec/helper-tcg.h" does with DEF_HELPER_FLAGS_N. @@ -964,57 +957,45 @@ static ffi_type *typecode_to_ffi(int argmask) g_assert_not_reached(); } -static void init_ffi_layouts(void) +static ffi_cif *init_ffi_layout(TCGHelperInfo *info) { - /* g_direct_hash/equal for direct comparisons on uint32_t. */ - GHashTable *ffi_table = g_hash_table_new(NULL, NULL); + unsigned typemask = info->typemask; + struct { + ffi_cif cif; + ffi_type *args[]; + } *ca; + ffi_status status; + int nargs; - for (int i = 0; i < ARRAY_SIZE(all_helpers); ++i) { - TCGHelperInfo *info = &all_helpers[i]; - unsigned typemask = info->typemask; - gpointer hash = (gpointer)(uintptr_t)typemask; - struct { - ffi_cif cif; - ffi_type *args[]; - } *ca; - ffi_status status; - int nargs; - ffi_cif *cif; + /* Ignoring the return type, find the last non-zero field. */ + nargs = 32 - clz32(typemask >> 3); + nargs = DIV_ROUND_UP(nargs, 3); + assert(nargs <= MAX_CALL_IARGS); - cif = g_hash_table_lookup(ffi_table, hash); - if (cif) { - info->cif = cif; - continue; + ca = g_malloc0(sizeof(*ca) + nargs * sizeof(ffi_type *)); + ca->cif.rtype = typecode_to_ffi(typemask & 7); + ca->cif.nargs = nargs; + + if (nargs != 0) { + ca->cif.arg_types = ca->args; + for (int j = 0; j < nargs; ++j) { + int typecode = extract32(typemask, (j + 1) * 3, 3); + ca->args[j] = typecode_to_ffi(typecode); } - - /* Ignoring the return type, find the last non-zero field. */ - nargs = 32 - clz32(typemask >> 3); - nargs = DIV_ROUND_UP(nargs, 3); - assert(nargs <= MAX_CALL_IARGS); - - ca = g_malloc0(sizeof(*ca) + nargs * sizeof(ffi_type *)); - ca->cif.rtype = typecode_to_ffi(typemask & 7); - ca->cif.nargs = nargs; - - if (nargs != 0) { - ca->cif.arg_types = ca->args; - for (int j = 0; j < nargs; ++j) { - int typecode = extract32(typemask, (j + 1) * 3, 3); - ca->args[j] = typecode_to_ffi(typecode); - } - } - - status = ffi_prep_cif(&ca->cif, FFI_DEFAULT_ABI, nargs, - ca->cif.rtype, ca->cif.arg_types); - assert(status == FFI_OK); - - cif = &ca->cif; - info->cif = cif; - g_hash_table_insert(ffi_table, hash, (gpointer)cif); } - g_hash_table_destroy(ffi_table); + status = ffi_prep_cif(&ca->cif, FFI_DEFAULT_ABI, nargs, + ca->cif.rtype, ca->cif.arg_types); + assert(status == FFI_OK); + + return &ca->cif; } + +#define HELPER_INFO_INIT(I) (&(I)->cif) +#define HELPER_INFO_INIT_VAL(I) init_ffi_layout(I) +#else +#define HELPER_INFO_INIT(I) (&(I)->init) +#define HELPER_INFO_INIT_VAL(I) 1 #endif /* CONFIG_TCG_INTERPRETER */ static inline bool arg_slot_reg_p(unsigned arg_slot) @@ -1327,16 +1308,6 @@ static void tcg_context_init(unsigned max_cpus) args_ct += n; } - /* Register helpers. */ - /* Use g_direct_hash/equal for direct pointer comparisons on func. */ - helper_table = g_hash_table_new(NULL, NULL); - - for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) { - init_call_layout(&all_helpers[i]); - g_hash_table_insert(helper_table, (gpointer)all_helpers[i].func, - (gpointer)&all_helpers[i]); - } - init_call_layout(&info_helper_ld32_mmu); init_call_layout(&info_helper_ld64_mmu); init_call_layout(&info_helper_ld128_mmu); @@ -1344,10 +1315,6 @@ static void tcg_context_init(unsigned max_cpus) init_call_layout(&info_helper_st64_mmu); init_call_layout(&info_helper_st128_mmu); -#ifdef CONFIG_TCG_INTERPRETER - init_ffi_layouts(); -#endif - tcg_target_init(s); process_op_defs(s); @@ -2141,15 +2108,18 @@ bool tcg_op_supported(TCGOpcode op) static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); -void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) +void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, int nargs, TCGTemp **args) { - const TCGHelperInfo *info; TCGv_i64 extend_free[MAX_CALL_IARGS]; int n_extend = 0; TCGOp *op; int i, n, pi = 0, total_args; - info = g_hash_table_lookup(helper_table, (gpointer)func); + if (unlikely(g_once_init_enter(HELPER_INFO_INIT(info)))) { + init_call_layout(info); + g_once_init_leave(HELPER_INFO_INIT(info), HELPER_INFO_INIT_VAL(info)); + } + total_args = info->nr_out + info->nr_in + 2; op = tcg_op_alloc(INDEX_op_call, total_args); @@ -2216,7 +2186,7 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) g_assert_not_reached(); } } - op->args[pi++] = (uintptr_t)func; + op->args[pi++] = (uintptr_t)info->func; op->args[pi++] = (uintptr_t)info; tcg_debug_assert(pi == total_args); From 177f648f0eacfd1a2b9a562a52297474b17b9adf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Mar 2023 08:09:03 -0700 Subject: [PATCH 091/412] tcg: Move temp_idx and tcgv_i32_temp debug out of line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes a multiplicity of calls to __assert_fail, saving up to 360kiB of .text space as measured on an x86_64 host. Old New Less %Change 9257272 8888680 368592 3.98% qemu-system-aarch64 6100968 5911832 189136 3.10% qemu-system-riscv64 5839112 5707032 132080 2.26% qemu-system-mips 4447608 4341752 105856 2.38% qemu-system-s390x Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 30 ++++++++++++++++-------------- tcg/tcg.c | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 34035dab81..64c10a63f3 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -630,13 +630,6 @@ static inline void *tcg_splitwx_to_rw(const void *rx) } #endif -static inline size_t temp_idx(TCGTemp *ts) -{ - ptrdiff_t n = ts - tcg_ctx->temps; - tcg_debug_assert(n >= 0 && n < tcg_ctx->nb_temps); - return n; -} - static inline TCGArg temp_arg(TCGTemp *ts) { return (uintptr_t)ts; @@ -647,16 +640,25 @@ static inline TCGTemp *arg_temp(TCGArg a) return (TCGTemp *)(uintptr_t)a; } -/* Using the offset of a temporary, relative to TCGContext, rather than - its index means that we don't use 0. That leaves offset 0 free for - a NULL representation without having to leave index 0 unused. */ +#ifdef CONFIG_DEBUG_TCG +size_t temp_idx(TCGTemp *ts); +TCGTemp *tcgv_i32_temp(TCGv_i32 v); +#else +static inline size_t temp_idx(TCGTemp *ts) +{ + return ts - tcg_ctx->temps; +} + +/* + * Using the offset of a temporary, relative to TCGContext, rather than + * its index means that we don't use 0. That leaves offset 0 free for + * a NULL representation without having to leave index 0 unused. + */ static inline TCGTemp *tcgv_i32_temp(TCGv_i32 v) { - uintptr_t o = (uintptr_t)v; - TCGTemp *t = (void *)tcg_ctx + o; - tcg_debug_assert(offsetof(TCGContext, temps[temp_idx(t)]) == o); - return t; + return (void *)tcg_ctx + (uintptr_t)v; } +#endif static inline TCGTemp *tcgv_i64_temp(TCGv_i64 v) { diff --git a/tcg/tcg.c b/tcg/tcg.c index ffd3ccaff7..59624fceec 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1800,6 +1800,25 @@ TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val) return tcg_constant_vec(t->base_type, vece, val); } +#ifdef CONFIG_DEBUG_TCG +size_t temp_idx(TCGTemp *ts) +{ + ptrdiff_t n = ts - tcg_ctx->temps; + assert(n >= 0 && n < tcg_ctx->nb_temps); + return n; +} + +TCGTemp *tcgv_i32_temp(TCGv_i32 v) +{ + uintptr_t o = (uintptr_t)v - offsetof(TCGContext, temps); + + assert(o < sizeof(TCGTemp) * tcg_ctx->nb_temps); + assert(o % sizeof(TCGTemp) == 0); + + return (void *)tcg_ctx + (uintptr_t)v; +} +#endif /* CONFIG_DEBUG_TCG */ + /* Return true if OP may appear in the opcode stream. Test the runtime variable that controls each opcode. */ bool tcg_op_supported(TCGOpcode op) From a3a692b8bf247ab28c70fc8dce967a84db90d55c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Mar 2023 22:14:36 -0700 Subject: [PATCH 092/412] tcg: Split tcg_gen_callN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make tcg_gen_callN a static function. Create tcg_gen_call[0-7] functions for use by helper-gen.h.inc. Removes a multiplicty of calls to __stack_chk_fail, saving up to 143kiB of .text space as measured on an x86_64 host. Old New Less %Change 8888680 8741816 146864 1.65% qemu-system-aarch64 5911832 5856152 55680 0.94% qemu-system-riscv64 5816728 5767512 49216 0.85% qemu-system-mips64 6707832 6659144 48688 0.73% qemu-system-ppc64 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/helper-gen.h | 40 ++++++++++++++--------------- include/tcg/tcg.h | 14 +++++++++- tcg/tcg.c | 54 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h index 248cff3351..784dd24ae2 100644 --- a/include/exec/helper-gen.h +++ b/include/exec/helper-gen.h @@ -17,7 +17,7 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ { \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 0, NULL); \ + tcg_gen_call0(&glue(helper_info_, name), dh_retvar(ret)); \ } #define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ @@ -25,8 +25,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1)) \ { \ - TCGTemp *args[1] = { dh_arg(t1, 1) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 1, args); \ + tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1)); \ } #define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ @@ -34,8 +34,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ { \ - TCGTemp *args[2] = { dh_arg(t1, 1), dh_arg(t2, 2) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 2, args); \ + tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2)); \ } #define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ @@ -43,8 +43,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ { \ - TCGTemp *args[3] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 3, args); \ + tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \ } #define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ @@ -53,9 +53,9 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ - TCGTemp *args[4] = { dh_arg(t1, 1), dh_arg(t2, 2), \ - dh_arg(t3, 3), dh_arg(t4, 4) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 4, args); \ + tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), \ + dh_arg(t3, 3), dh_arg(t4, 4)); \ } #define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ @@ -64,9 +64,9 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \ { \ - TCGTemp *args[5] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 5, args); \ + tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5)); \ } #define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ @@ -75,9 +75,9 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \ { \ - TCGTemp *args[6] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 6, args); \ + tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6)); \ } #define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7)\ @@ -87,10 +87,10 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ dh_arg_decl(t7, 7)) \ { \ - TCGTemp *args[7] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ - dh_arg(t7, 7) }; \ - tcg_gen_callN(&glue(helper_info_, name), dh_retvar(ret), 7, args); \ + tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ + dh_arg(t7, 7)); \ } #include "helper.h" diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 64c10a63f3..7c1bbba673 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -939,7 +939,19 @@ typedef struct TCGTargetOpDef { bool tcg_op_supported(TCGOpcode op); -void tcg_gen_callN(TCGHelperInfo *, TCGTemp *ret, int nargs, TCGTemp **args); +void tcg_gen_call0(TCGHelperInfo *, TCGTemp *ret); +void tcg_gen_call1(TCGHelperInfo *, TCGTemp *ret, TCGTemp *); +void tcg_gen_call2(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *); +void tcg_gen_call3(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, + TCGTemp *, TCGTemp *); +void tcg_gen_call4(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *); +void tcg_gen_call5(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call6(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call7(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs); void tcg_op_remove(TCGContext *s, TCGOp *op); diff --git a/tcg/tcg.c b/tcg/tcg.c index 59624fceec..d369367c5a 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2127,7 +2127,7 @@ bool tcg_op_supported(TCGOpcode op) static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); -void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, int nargs, TCGTemp **args) +static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) { TCGv_i64 extend_free[MAX_CALL_IARGS]; int n_extend = 0; @@ -2217,6 +2217,58 @@ void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, int nargs, TCGTemp **args) } } +void tcg_gen_call0(TCGHelperInfo *info, TCGTemp *ret) +{ + tcg_gen_callN(info, ret, NULL); +} + +void tcg_gen_call1(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) +{ + tcg_gen_callN(info, ret, &t1); +} + +void tcg_gen_call2(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2) +{ + TCGTemp *args[2] = { t1, t2 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call3(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3) +{ + TCGTemp *args[3] = { t1, t2, t3 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call4(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3, TCGTemp *t4) +{ + TCGTemp *args[4] = { t1, t2, t3, t4 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call5(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, TCGTemp *t5) +{ + TCGTemp *args[5] = { t1, t2, t3, t4, t5 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call6(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2, + TCGTemp *t3, TCGTemp *t4, TCGTemp *t5, TCGTemp *t6) +{ + TCGTemp *args[6] = { t1, t2, t3, t4, t5, t6 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call7(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, + TCGTemp *t5, TCGTemp *t6, TCGTemp *t7) +{ + TCGTemp *args[7] = { t1, t2, t3, t4, t5, t6, t7 }; + tcg_gen_callN(info, ret, args); +} + static void tcg_reg_alloc_start(TCGContext *s) { int i, n; From e4eff8e4edc188b5eee8834479b515325889e27a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Mar 2023 19:07:00 -0700 Subject: [PATCH 093/412] tcg: Split helper-gen.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create helper-gen-common.h without the target specific portion. Use that in tcg-op-common.h. Reorg headers in target/arm to ensure that helper-gen.h is included before helper-info.c.inc. All other targets are already correct in this regard. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- MAINTAINERS | 1 + include/exec/helper-gen-common.h | 18 ++++++ include/exec/helper-gen.h | 101 ++---------------------------- include/exec/helper-gen.h.inc | 102 +++++++++++++++++++++++++++++++ include/tcg/tcg-op-common.h | 2 +- target/arm/tcg/translate.c | 8 +-- 6 files changed, 129 insertions(+), 103 deletions(-) create mode 100644 include/exec/helper-gen-common.h create mode 100644 include/exec/helper-gen.h.inc diff --git a/MAINTAINERS b/MAINTAINERS index a1b8376f4c..2366f64d3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -154,6 +154,7 @@ F: include/exec/exec-all.h F: include/exec/tb-flush.h F: include/exec/target_long.h F: include/exec/helper*.h +F: include/exec/helper*.h.inc F: include/exec/helper-info.c.inc F: include/sysemu/cpus.h F: include/sysemu/tcg.h diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h new file mode 100644 index 0000000000..5d6d78a625 --- /dev/null +++ b/include/exec/helper-gen-common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands generation functions for tcg opcodes. + */ + +#ifndef HELPER_GEN_COMMON_H +#define HELPER_GEN_COMMON_H + +#define HELPER_H "accel/tcg/tcg-runtime.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H + +#define HELPER_H "accel/tcg/plugin-helpers.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H + +#endif /* HELPER_GEN_COMMON_H */ diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h index 784dd24ae2..f7ec155699 100644 --- a/include/exec/helper-gen.h +++ b/include/exec/helper-gen.h @@ -2,108 +2,15 @@ /* * Helper file for declaring TCG helper functions. * This one expands generation functions for tcg opcodes. - * Define HELPER_H for the header file to be expanded, - * and static inline to change from global file scope. */ #ifndef HELPER_GEN_H #define HELPER_GEN_H -#include "tcg/tcg.h" -#include "tcg/helper-info.h" -#include "exec/helper-head.h" +#include "exec/helper-gen-common.h" -#define DEF_HELPER_FLAGS_0(name, flags, ret) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ -{ \ - tcg_gen_call0(&glue(helper_info_, name), dh_retvar(ret)); \ -} - -#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1)) \ -{ \ - tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1)); \ -} - -#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ -{ \ - tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1), dh_arg(t2, 2)); \ -} - -#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ -{ \ - tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \ -} - -#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ - dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ -{ \ - tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1), dh_arg(t2, 2), \ - dh_arg(t3, 3), dh_arg(t4, 4)); \ -} - -#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ - dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \ -{ \ - tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5)); \ -} - -#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ - dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \ -{ \ - tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6)); \ -} - -#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7)\ -extern TCGHelperInfo glue(helper_info_, name); \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ - dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ - dh_arg_decl(t7, 7)) \ -{ \ - tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \ - dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ - dh_arg(t7, 7)); \ -} - -#include "helper.h" -#include "accel/tcg/tcg-runtime.h" -#include "accel/tcg/plugin-helpers.h" - -#undef DEF_HELPER_FLAGS_0 -#undef DEF_HELPER_FLAGS_1 -#undef DEF_HELPER_FLAGS_2 -#undef DEF_HELPER_FLAGS_3 -#undef DEF_HELPER_FLAGS_4 -#undef DEF_HELPER_FLAGS_5 -#undef DEF_HELPER_FLAGS_6 -#undef DEF_HELPER_FLAGS_7 +#define HELPER_H "helper.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H #endif /* HELPER_GEN_H */ diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc new file mode 100644 index 0000000000..c009641517 --- /dev/null +++ b/include/exec/helper-gen.h.inc @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands generation functions for tcg opcodes. + * Define HELPER_H for the header file to be expanded, + * and static inline to change from global file scope. + */ + +#include "tcg/tcg.h" +#include "tcg/helper-info.h" +#include "exec/helper-head.h" + +#define DEF_HELPER_FLAGS_0(name, flags, ret) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ +{ \ + tcg_gen_call0(&glue(helper_info_, name), dh_retvar(ret)); \ +} + +#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1)) \ +{ \ + tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1)); \ +} + +#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ +{ \ + tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2)); \ +} + +#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ +{ \ + tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \ +} + +#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ + dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ +{ \ + tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), \ + dh_arg(t3, 3), dh_arg(t4, 4)); \ +} + +#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \ +{ \ + tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5)); \ +} + +#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \ +{ \ + tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6)); \ +} + +#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7)\ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ + dh_arg_decl(t7, 7)) \ +{ \ + tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ + dh_arg(t7, 7)); \ +} + +#include HELPER_H + +#undef DEF_HELPER_FLAGS_0 +#undef DEF_HELPER_FLAGS_1 +#undef DEF_HELPER_FLAGS_2 +#undef DEF_HELPER_FLAGS_3 +#undef DEF_HELPER_FLAGS_4 +#undef DEF_HELPER_FLAGS_5 +#undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 04a9ca1fc6..f6f05469c5 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -10,7 +10,7 @@ #include "tcg/tcg.h" #include "exec/helper-proto.h" -#include "exec/helper-gen.h" +#include "exec/helper-gen-common.h" /* Basic output routines. Not for general consumption. */ diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 4d84850d74..ce50531dff 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -32,6 +32,9 @@ #include "semihosting/semihost.h" #include "exec/log.h" #include "cpregs.h" +#include "translate.h" +#include "translate-a32.h" +#include "exec/gen-icount.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -48,9 +51,6 @@ #define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) #define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) -#include "translate.h" -#include "translate-a32.h" - /* These are TCG temporaries used only by the legacy iwMMXt decoder */ static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; /* These are TCG globals which alias CPUARMState fields */ @@ -59,8 +59,6 @@ TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; TCGv_i64 cpu_exclusive_addr; TCGv_i64 cpu_exclusive_val; -#include "exec/gen-icount.h" - static const char * const regnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; From c213ee2dfc7365c0c8544fa25672d891fdffe343 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Mar 2023 20:13:36 -0700 Subject: [PATCH 094/412] tcg: Split helper-proto.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create helper-proto-common.h without the target specific portion. Use that in tcg-op-common.h. Include helper-proto.h in target/arm and target/hexagon before helper-info.c.inc; all other targets are already correct in this regard. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 3 +- accel/tcg/plugin-gen.c | 2 +- accel/tcg/tcg-runtime-gvec.c | 2 +- accel/tcg/tcg-runtime.c | 2 +- include/exec/helper-proto-common.h | 18 ++++++++ include/exec/helper-proto.h | 73 ++++-------------------------- include/exec/helper-proto.h.inc | 68 ++++++++++++++++++++++++++++ include/tcg/tcg-op-common.h | 2 +- target/arm/tcg/translate.c | 1 + target/hexagon/translate.c | 1 + 10 files changed, 102 insertions(+), 70 deletions(-) create mode 100644 include/exec/helper-proto-common.h create mode 100644 include/exec/helper-proto.h.inc diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 32a4817139..5e2ca47243 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -29,7 +29,7 @@ #include "tcg/tcg.h" #include "qemu/error-report.h" #include "exec/log.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "qemu/atomic.h" #include "qemu/atomic128.h" #include "exec/translate-all.h" @@ -41,7 +41,6 @@ #endif #include "tcg/tcg-ldst.h" #include "tcg/oversized-guest.h" -#include "exec/helper-proto.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 40b34a0403..3e528f191d 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -49,7 +49,7 @@ #include "exec/exec-all.h" #include "exec/plugin-gen.h" #include "exec/translator.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #define HELPER_H "accel/tcg/plugin-helpers.h" #include "exec/helper-info.c.inc" diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 97399493d5..6c99f952ca 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "cpu.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "tcg/tcg-gvec-desc.h" diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 14b59a36e5..9fa539ad3d 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "cpu.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "disas/disas.h" diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h new file mode 100644 index 0000000000..4d4b022668 --- /dev/null +++ b/include/exec/helper-proto-common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands prototypes for the helper functions. + */ + +#ifndef HELPER_PROTO_COMMON_H +#define HELPER_PROTO_COMMON_H + +#define HELPER_H "accel/tcg/tcg-runtime.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H + +#define HELPER_H "accel/tcg/plugin-helpers.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H + +#endif /* HELPER_PROTO_COMMON_H */ diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h index 7a3f04b58c..6935cb4f16 100644 --- a/include/exec/helper-proto.h +++ b/include/exec/helper-proto.h @@ -1,71 +1,16 @@ -/* Helper file for declaring TCG helper functions. - This one expands prototypes for the helper functions. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands prototypes for the helper functions. + */ #ifndef HELPER_PROTO_H #define HELPER_PROTO_H -#include "exec/helper-head.h" +#include "exec/helper-proto-common.h" -/* - * Work around an issue with --enable-lto, in which GCC's ipa-split pass - * decides to split out the noreturn code paths that raise an exception, - * taking the __builtin_return_address() along into the new function, - * where it no longer computes a value that returns to TCG generated code. - * Despite the name, the noinline attribute affects splitter, so this - * prevents the optimization in question. Given that helpers should not - * otherwise be called directly, this should have any other visible effect. - * - * See https://gitlab.com/qemu-project/qemu/-/issues/1454 - */ -#define DEF_HELPER_ATTR __attribute__((noinline)) - -#define DEF_HELPER_FLAGS_0(name, flags, ret) \ -dh_ctype(ret) HELPER(name) (void) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1)) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), \ - dh_ctype(t3)) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4)) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5)) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5), \ - dh_ctype(t6)) DEF_HELPER_ATTR; - -#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5), dh_ctype(t6), \ - dh_ctype(t7)) DEF_HELPER_ATTR; - -#define IN_HELPER_PROTO - -#include "helper.h" -#include "accel/tcg/tcg-runtime.h" -#include "accel/tcg/plugin-helpers.h" - -#undef IN_HELPER_PROTO - -#undef DEF_HELPER_FLAGS_0 -#undef DEF_HELPER_FLAGS_1 -#undef DEF_HELPER_FLAGS_2 -#undef DEF_HELPER_FLAGS_3 -#undef DEF_HELPER_FLAGS_4 -#undef DEF_HELPER_FLAGS_5 -#undef DEF_HELPER_FLAGS_6 -#undef DEF_HELPER_FLAGS_7 -#undef DEF_HELPER_ATTR +#define HELPER_H "helper.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H #endif /* HELPER_PROTO_H */ diff --git a/include/exec/helper-proto.h.inc b/include/exec/helper-proto.h.inc new file mode 100644 index 0000000000..c3aa666929 --- /dev/null +++ b/include/exec/helper-proto.h.inc @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands prototypes for the helper functions. + * Define HELPER_H for the header file to be expanded. + */ + +#include "exec/helper-head.h" + +/* + * Work around an issue with --enable-lto, in which GCC's ipa-split pass + * decides to split out the noreturn code paths that raise an exception, + * taking the __builtin_return_address() along into the new function, + * where it no longer computes a value that returns to TCG generated code. + * Despite the name, the noinline attribute affects splitter, so this + * prevents the optimization in question. Given that helpers should not + * otherwise be called directly, this should not have any other visible effect. + * + * See https://gitlab.com/qemu-project/qemu/-/issues/1454 + */ +#define DEF_HELPER_ATTR __attribute__((noinline)) + +#define DEF_HELPER_FLAGS_0(name, flags, ret) \ +dh_ctype(ret) HELPER(name) (void) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), \ + dh_ctype(t3)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5), \ + dh_ctype(t6)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5), dh_ctype(t6), \ + dh_ctype(t7)) DEF_HELPER_ATTR; + +#define IN_HELPER_PROTO + +#include HELPER_H + +#undef IN_HELPER_PROTO + +#undef DEF_HELPER_FLAGS_0 +#undef DEF_HELPER_FLAGS_1 +#undef DEF_HELPER_FLAGS_2 +#undef DEF_HELPER_FLAGS_3 +#undef DEF_HELPER_FLAGS_4 +#undef DEF_HELPER_FLAGS_5 +#undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 +#undef DEF_HELPER_ATTR diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index f6f05469c5..be382bbf77 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -9,7 +9,7 @@ #define TCG_TCG_OP_COMMON_H #include "tcg/tcg.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "exec/helper-gen-common.h" /* Basic output routines. Not for general consumption. */ diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index ce50531dff..379f266256 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -35,6 +35,7 @@ #include "translate.h" #include "translate-a32.h" #include "exec/gen-icount.h" +#include "exec/helper-proto.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 00e25035ce..770de58647 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -21,6 +21,7 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" +#include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "internal.h" From e03291cd9a9f511a70a9164bbe8673ed1e9de360 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 3 Jun 2023 09:48:07 -0700 Subject: [PATCH 095/412] target/sh4: Emit insn_start for each insn in gUSA region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an assert in tcg_gen_code that we don't accidentally eliminate an insn_start during optimization. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sh4/translate.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 9d2c7a3337..76f46d268b 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2146,9 +2146,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) /* The entire region has been translated. */ ctx->envflags &= ~TB_FLAG_GUSA_MASK; - ctx->base.pc_next = pc_end; - ctx->base.num_insns += max_insns - 1; - return; + goto done; fail: qemu_log_mask(LOG_UNIMP, "Unrecognized gUSA sequence %08x-%08x\n", @@ -2165,8 +2163,19 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) purposes of accounting within the TB. We might as well report the entire region consumed via ctx->base.pc_next so that it's immediately available in the disassembly dump. */ + + done: ctx->base.pc_next = pc_end; ctx->base.num_insns += max_insns - 1; + + /* + * Emit insn_start to cover each of the insns in the region. + * This matches an assert in tcg.c making sure that we have + * tb->icount * insn_start. + */ + for (i = 1; i < max_insns; ++i) { + tcg_gen_insn_start(pc + i * 2, ctx->envflags); + } } #endif From 747bd69d0f6d278923c50a3be6dd9b85e5dfd603 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Mar 2023 21:30:31 -0700 Subject: [PATCH 096/412] tcg: Add insn_start_words to TCGContext This will enable replacement of TARGET_INSN_START_WORDS in tcg.c. Split out "tcg/insn-start-words.h" and use it in target/. Reviewed-by: Anton Johansson Signed-off-by: Richard Henderson --- accel/tcg/perf.c | 8 ++++++-- accel/tcg/translate-all.c | 20 +++++++++++++------- include/tcg/insn-start-words.h | 17 +++++++++++++++++ include/tcg/tcg-op.h | 8 ++++---- include/tcg/tcg-opc.h | 6 +++--- include/tcg/tcg.h | 9 ++------- target/i386/helper.c | 2 +- target/openrisc/sys_helper.c | 2 +- tcg/tcg.c | 16 +++++++++++----- 9 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 include/tcg/insn-start-words.h diff --git a/accel/tcg/perf.c b/accel/tcg/perf.c index 65e35ea3b9..f5a1eda39f 100644 --- a/accel/tcg/perf.c +++ b/accel/tcg/perf.c @@ -311,7 +311,8 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, const void *start) { struct debuginfo_query *q; - size_t insn; + size_t insn, start_words; + uint64_t *gen_insn_data; if (!perfmap && !jitdump) { return; @@ -325,9 +326,12 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, debuginfo_lock(); /* Query debuginfo for each guest instruction. */ + gen_insn_data = tcg_ctx->gen_insn_data; + start_words = tcg_ctx->insn_start_words; + for (insn = 0; insn < tb->icount; insn++) { /* FIXME: This replicates the restore_state_to_opc() logic. */ - q[insn].address = tcg_ctx->gen_insn_data[insn][0]; + q[insn].address = gen_insn_data[insn * start_words + 0]; if (tb_cflags(tb) & CF_PCREL) { q[insn].address |= (guest_pc & TARGET_PAGE_MASK); } else { diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 5cea9acd5a..67b838e16b 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -64,6 +64,7 @@ #include "tb-context.h" #include "internal.h" #include "perf.h" +#include "tcg/insn-start-words.h" TBContext tb_ctx; @@ -127,22 +128,26 @@ static int64_t decode_sleb128(const uint8_t **pp) static int encode_search(TranslationBlock *tb, uint8_t *block) { uint8_t *highwater = tcg_ctx->code_gen_highwater; + uint64_t *insn_data = tcg_ctx->gen_insn_data; + uint16_t *insn_end_off = tcg_ctx->gen_insn_end_off; uint8_t *p = block; int i, j, n; for (i = 0, n = tb->icount; i < n; ++i) { - uint64_t prev; + uint64_t prev, curr; for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { if (i == 0) { prev = (!(tb_cflags(tb) & CF_PCREL) && j == 0 ? tb->pc : 0); } else { - prev = tcg_ctx->gen_insn_data[i - 1][j]; + prev = insn_data[(i - 1) * TARGET_INSN_START_WORDS + j]; } - p = encode_sleb128(p, tcg_ctx->gen_insn_data[i][j] - prev); + curr = insn_data[i * TARGET_INSN_START_WORDS + j]; + p = encode_sleb128(p, curr - prev); } - prev = (i == 0 ? 0 : tcg_ctx->gen_insn_end_off[i - 1]); - p = encode_sleb128(p, tcg_ctx->gen_insn_end_off[i] - prev); + prev = (i == 0 ? 0 : insn_end_off[i - 1]); + curr = insn_end_off[i]; + p = encode_sleb128(p, curr - prev); /* Test for (pending) buffer overflow. The assumption is that any one row beginning below the high water mark cannot overrun @@ -358,6 +363,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->tlb_fast_offset = (int)offsetof(ArchCPU, neg.tlb.f) - (int)offsetof(ArchCPU, env); #endif + tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; tb_overflow: @@ -451,7 +457,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, fprintf(logfile, "OUT: [size=%d]\n", gen_code_size); fprintf(logfile, " -- guest addr 0x%016" PRIx64 " + tb prologue\n", - tcg_ctx->gen_insn_data[insn][0]); + tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); chunk_start = tcg_ctx->gen_insn_end_off[insn]; disas(logfile, tb->tc.ptr, chunk_start); @@ -464,7 +470,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; if (chunk_end > chunk_start) { fprintf(logfile, " -- guest addr 0x%016" PRIx64 "\n", - tcg_ctx->gen_insn_data[insn][0]); + tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); disas(logfile, tb->tc.ptr + chunk_start, chunk_end - chunk_start); chunk_start = chunk_end; diff --git a/include/tcg/insn-start-words.h b/include/tcg/insn-start-words.h new file mode 100644 index 0000000000..50c18bd326 --- /dev/null +++ b/include/tcg/insn-start-words.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define TARGET_INSN_START_WORDS + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TARGET_INSN_START_WORDS + +#include "cpu.h" + +#ifndef TARGET_INSN_START_EXTRA_WORDS +# define TARGET_INSN_START_WORDS 1 +#else +# define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) +#endif + +#endif /* TARGET_INSN_START_WORDS */ diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 47f1dce816..d63683c47b 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -22,20 +22,20 @@ # error #endif -#if TARGET_INSN_START_WORDS == 1 +#ifndef TARGET_INSN_START_EXTRA_WORDS static inline void tcg_gen_insn_start(target_ulong pc) { TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 64 / TCG_TARGET_REG_BITS); tcg_set_insn_start_param(op, 0, pc); } -#elif TARGET_INSN_START_WORDS == 2 +#elif TARGET_INSN_START_EXTRA_WORDS == 1 static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) { TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 2 * 64 / TCG_TARGET_REG_BITS); tcg_set_insn_start_param(op, 0, pc); tcg_set_insn_start_param(op, 1, a1); } -#elif TARGET_INSN_START_WORDS == 3 +#elif TARGET_INSN_START_EXTRA_WORDS == 2 static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, target_ulong a2) { @@ -45,7 +45,7 @@ static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, tcg_set_insn_start_param(op, 2, a2); } #else -# error "Unhandled number of operands to insn_start" +#error Unhandled TARGET_INSN_START_EXTRA_WORDS value #endif #if TARGET_LONG_BITS == 32 diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 21594c1590..acfa5ba753 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -188,9 +188,9 @@ DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64)) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) -/* QEMU specific */ -DEF(insn_start, 0, 0, DATA64_ARGS * TARGET_INSN_START_WORDS, - TCG_OPF_NOT_PRESENT) +/* There are tcg_ctx->insn_start_words here, not just one. */ +DEF(insn_start, 0, 0, DATA64_ARGS, TCG_OPF_NOT_PRESENT) + DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 7c1bbba673..813c733910 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -173,12 +173,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_v256 0 #endif -#ifndef TARGET_INSN_START_EXTRA_WORDS -# define TARGET_INSN_START_WORDS 1 -#else -# define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) -#endif - typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg/tcg-opc.h" @@ -526,6 +520,7 @@ struct TCGContext { uint8_t page_bits; uint8_t tlb_dyn_max_bits; #endif + uint8_t insn_start_words; TCGRegSet reserved_regs; intptr_t current_frame_offset; @@ -597,7 +592,7 @@ struct TCGContext { TCGTemp *reg_to_temp[TCG_TARGET_NB_REGS]; uint16_t gen_insn_end_off[TCG_MAX_INSNS]; - uint64_t gen_insn_data[TCG_MAX_INSNS][TARGET_INSN_START_WORDS]; + uint64_t *gen_insn_data; /* Exit to translator on overflow. */ sigjmp_buf jmp_trans; diff --git a/target/i386/helper.c b/target/i386/helper.c index 682d10d98a..36bf2107e7 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -29,7 +29,7 @@ #endif #include "qemu/log.h" #ifdef CONFIG_TCG -#include "tcg/tcg.h" +#include "tcg/insn-start-words.h" #endif void cpu_sync_avx_hflag(CPUX86State *env) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 110f157601..782a5751b7 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -26,7 +26,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #endif -#include "tcg/tcg.h" +#include "tcg/insn-start-words.h" #define TO_SPR(group, number) (((group) << 11) + (number)) diff --git a/tcg/tcg.c b/tcg/tcg.c index d369367c5a..a339e3e3d8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1501,6 +1501,8 @@ void tcg_func_start(TCGContext *s) tcg_debug_assert(s->tlb_fast_offset < 0); tcg_debug_assert(s->tlb_fast_offset >= MIN_TLB_MASK_TABLE_OFS); #endif + + tcg_debug_assert(s->insn_start_words > 0); } static TCGTemp *tcg_temp_alloc(TCGContext *s) @@ -2445,7 +2447,7 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) nb_oargs = 0; col += ne_fprintf(f, "\n ----"); - for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { + for (i = 0, k = s->insn_start_words; i < k; ++i) { col += ne_fprintf(f, " %016" PRIx64, tcg_get_insn_start_param(op, i)); } @@ -6024,7 +6026,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) #ifdef CONFIG_PROFILER TCGProfile *prof = &s->prof; #endif - int i, num_insns; + int i, start_words, num_insns; TCGOp *op; #ifdef CONFIG_PROFILER @@ -6147,6 +6149,10 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) s->pool_labels = NULL; #endif + start_words = s->insn_start_words; + s->gen_insn_data = + tcg_malloc(sizeof(uint64_t) * s->gen_tb->icount * start_words); + num_insns = -1; QTAILQ_FOREACH(op, &s->ops, link) { TCGOpcode opc = op->opc; @@ -6172,8 +6178,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) assert(s->gen_insn_end_off[num_insns] == off); } num_insns++; - for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { - s->gen_insn_data[num_insns][i] = + for (i = 0; i < start_words; ++i) { + s->gen_insn_data[num_insns * start_words + i] = tcg_get_insn_start_param(op, i); } break; @@ -6219,7 +6225,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) return -2; } } - tcg_debug_assert(num_insns >= 0); + tcg_debug_assert(num_insns + 1 == s->gen_tb->icount); s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); /* Generate TB finalization at the end of block */ From 28ea568a039f7c8c8df168800602725062f6dd5c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Mar 2023 22:56:55 -0700 Subject: [PATCH 097/412] tcg: Add guest_mo to TCGContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces of TCG_GUEST_DEFAULT_MO in tcg-op-ldst.c. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 5 +++++ include/tcg/tcg.h | 1 + tcg/tcg-op-ldst.c | 4 +--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 67b838e16b..200de2793c 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -364,6 +364,11 @@ TranslationBlock *tb_gen_code(CPUState *cpu, (int)offsetof(ArchCPU, neg.tlb.f) - (int)offsetof(ArchCPU, env); #endif tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; +#ifdef TCG_GUEST_DEFAULT_MO + tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO; +#else + tcg_ctx->guest_mo = TCG_MO_ALL; +#endif tb_overflow: diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 813c733910..9f607e2664 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -521,6 +521,7 @@ struct TCGContext { uint8_t tlb_dyn_max_bits; #endif uint8_t insn_start_words; + TCGBar guest_mo; TCGRegSet reserved_regs; intptr_t current_frame_offset; diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 3c00bf0c95..9bcf63b041 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -104,9 +104,7 @@ static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) static void tcg_gen_req_mo(TCGBar type) { -#ifdef TCG_GUEST_DEFAULT_MO - type &= TCG_GUEST_DEFAULT_MO; -#endif + type &= tcg_ctx->guest_mo; type &= ~TCG_TARGET_DEFAULT_MO; if (type) { tcg_gen_mb(type | TCG_BAR_SC); From 3a80bde37b15593a4ed2a045b6d2412b4aa970e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 09:26:35 -0700 Subject: [PATCH 098/412] tcg: Move TLB_FLAGS_MASK check out of get_alignment_bits The replacement isn't ideal, as the raw count of bits is not easily synced with exec/cpu-all.h, but it does remove from tcg.h the target dependency on TARGET_PAGE_BITS_MIN which is built into TLB_FLAGS_MASK. Reviewed-by: Anton Johansson Signed-off-by: Richard Henderson --- include/exec/cpu-all.h | 3 +++ include/tcg/tcg.h | 4 ---- tcg/tcg-op-ldst.c | 18 ++++++++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 78d258af44..09bf4c0cc6 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -314,6 +314,9 @@ CPUArchState *cpu_copy(CPUArchState *env); * * Use TARGET_PAGE_BITS_MIN so that these bits are constant * when TARGET_PAGE_BITS_VARY is in effect. + * + * The count, if not the placement of these bits is known + * to tcg/tcg-op-ldst.c, check_max_alignment(). */ /* Zero if TLB entry is valid. */ #define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 9f607e2664..635fa53fdb 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -305,10 +305,6 @@ static inline unsigned get_alignment_bits(MemOp memop) /* A specific alignment requirement. */ a = a >> MO_ASHIFT; } -#if defined(CONFIG_SOFTMMU) - /* The requested alignment cannot overlap the TLB flags. */ - tcg_debug_assert((TLB_FLAGS_MASK & ((1 << a) - 1)) == 0); -#endif return a; } diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 9bcf63b041..46a5977b35 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -32,11 +32,23 @@ #include "tcg-internal.h" -static inline MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) +static void check_max_alignment(unsigned a_bits) +{ +#if defined(CONFIG_SOFTMMU) + /* + * The requested alignment cannot overlap the TLB flags. + * FIXME: Must keep the count up-to-date with "exec/cpu-all.h". + */ + tcg_debug_assert(a_bits + 6 <= tcg_ctx->page_bits); +#endif +} + +static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) { - /* Trigger the asserts within as early as possible. */ unsigned a_bits = get_alignment_bits(op); + check_max_alignment(a_bits); + /* Prefer MO_ALIGN+MO_XX over MO_ALIGN_XX+MO_XX */ if (a_bits == (op & MO_SIZE)) { op = (op & ~MO_AMASK) | MO_ALIGN; @@ -491,6 +503,7 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, TCGv_i64 ext_addr = NULL; TCGOpcode opc; + check_max_alignment(get_alignment_bits(memop)); tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); /* TODO: For now, force 32-bit hosts to use the helper. */ @@ -599,6 +612,7 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, TCGv_i64 ext_addr = NULL; TCGOpcode opc; + check_max_alignment(get_alignment_bits(memop)); tcg_gen_req_mo(TCG_MO_ST_LD | TCG_MO_ST_ST); /* TODO: For now, force 32-bit hosts to use the helper. */ From 447ca1cbfaddd73a0b0a3a18b1dab993f9066249 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 10:15:11 -0700 Subject: [PATCH 099/412] tcg: Split tcg/tcg-op-gvec.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create tcg/tcg-op-gvec-common.h, moving everything that does not concern TARGET_LONG_BITS. Adjust tcg-op-gvec.c to use the new header. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 426 +++++++++++++++++++++++++++++ include/tcg/tcg-op-gvec.h | 444 +------------------------------ tcg/tcg-op-gvec.c | 2 +- 3 files changed, 437 insertions(+), 435 deletions(-) create mode 100644 include/tcg/tcg-op-gvec-common.h diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h new file mode 100644 index 0000000000..e2683d487f --- /dev/null +++ b/include/tcg/tcg-op-gvec-common.h @@ -0,0 +1,426 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Target independent generic vector operation expansion + * + * Copyright (c) 2018 Linaro + */ + +#ifndef TCG_TCG_OP_GVEC_COMMON_H +#define TCG_TCG_OP_GVEC_COMMON_H + +/* + * "Generic" vectors. All operands are given as offsets from ENV, + * and therefore cannot also be allocated via tcg_global_mem_new_*. + * OPRSZ is the byte size of the vector upon which the operation is performed. + * MAXSZ is the byte size of the full vector; bytes beyond OPSZ are cleared. + * + * All sizes must be 8 or any multiple of 16. + * When OPRSZ is 8, the alignment may be 8, otherwise must be 16. + * Operands may completely, but not partially, overlap. + */ + +/* Expand a call to a gvec-style helper, with pointers to two vector + operands, and a descriptor (see tcg-gvec-desc.h). */ +typedef void gen_helper_gvec_2(TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_2 *fn); + +/* Similarly, passing an extra data value. */ +typedef void gen_helper_gvec_2i(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); +void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_2i *fn); + +/* Similarly, passing an extra pointer (e.g. env or float_status). */ +typedef void gen_helper_gvec_2_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_2_ptr(uint32_t dofs, uint32_t aofs, + TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_2_ptr *fn); + +/* Similarly, with three vector operands. */ +typedef void gen_helper_gvec_3(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_3 *fn); + +/* Similarly, with four vector operands. */ +typedef void gen_helper_gvec_4(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_4 *fn); + +/* Similarly, with five vector operands. */ +typedef void gen_helper_gvec_5(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_5_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t xofs, uint32_t oprsz, + uint32_t maxsz, int32_t data, gen_helper_gvec_5 *fn); + +typedef void gen_helper_gvec_3_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_3_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_3_ptr *fn); + +typedef void gen_helper_gvec_4_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, TCGv_ptr ptr, uint32_t oprsz, + uint32_t maxsz, int32_t data, + gen_helper_gvec_4_ptr *fn); + +typedef void gen_helper_gvec_5_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_5_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t eofs, TCGv_ptr ptr, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_5_ptr *fn); + +/* Expand a gvec operation. Either inline or out-of-line depending on + the actual vector size and the operations supported by the host. */ +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_2 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + int32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 2nd source operand. */ + bool load_dest; +} GVecGen2; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_2 *fno; + /* Expand out-of-line helper w/descriptor, data as argument. */ + gen_helper_gvec_2i *fnoi; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 3rd source operand. */ + bool load_dest; +} GVecGen2i; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_2i *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + uint32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load scalar as 1st source operand. */ + bool scalar_first; +} GVecGen2s; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_3 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + int32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 3rd source operand. */ + bool load_dest; +} GVecGen3; + +typedef struct { + /* + * Expand inline as a 64-bit or 32-bit integer. Only one of these will be + * non-NULL. + */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_3 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 3rd source operand. */ + bool load_dest; +} GVecGen3i; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_4 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + int32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Write aofs as a 2nd dest operand. */ + bool write_aofs; +} GVecGen4; + +typedef struct { + /* + * Expand inline as a 64-bit or 32-bit integer. Only one of these will be + * non-NULL. + */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_4 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; +} GVecGen4i; + +void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); +void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, + uint32_t maxsz, int64_t c, const GVecGen2i *); +void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, + uint32_t maxsz, TCGv_i64 c, const GVecGen2s *); +void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); +void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, int64_t c, + const GVecGen3i *); +void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen4 *); +void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz, int64_t c, + const GVecGen4i *); + +/* Expand a specific vector operation. */ + +void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_abs(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_mul(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_addi(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_muli(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_adds(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_subs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_muls(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); + +/* Saturated arithmetic. */ +void tcg_gen_gvec_ssadd(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sssub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +/* Min/max. */ +void tcg_gen_gvec_smin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_umin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_smax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_and(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_xor(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_nand(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_nor(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_eqv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_andi(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_xori(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_ori(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_ands(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_andcs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_xors(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_ors(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t s, uint32_t m); +void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t s, + uint32_t m, uint64_t imm); +void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t s, + uint32_t m, TCGv_i32); +void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t s, + uint32_t m, TCGv_i64); + +void tcg_gen_gvec_shli(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_shri(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sari(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotli(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotri(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_shls(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_shrs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sars(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotls(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotrs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); + +/* + * Perform vector shift by vector element, modulo the element size. + * E.g. D[i] = A[i] << (B[i] % (8 << vece)). + */ +void tcg_gen_gvec_shlv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_shrv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sarv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotlv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotrv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, + uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz); + +/* + * Perform vector bit select: d = (b & a) | (c & ~a). + */ +void tcg_gen_gvec_bitsel(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz); + +/* + * 64-bit vector operations. Use these when the register has been allocated + * with tcg_global_mem_new_i64, and so we cannot also address it via pointer. + * OPRSZ = MAXSZ = 8. + */ + +void tcg_gen_vec_neg8_i64(TCGv_i64 d, TCGv_i64 a); +void tcg_gen_vec_neg16_i64(TCGv_i64 d, TCGv_i64 a); +void tcg_gen_vec_neg32_i64(TCGv_i64 d, TCGv_i64 a); + +void tcg_gen_vec_add8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_add16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); + +void tcg_gen_vec_sub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_sub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); + +void tcg_gen_vec_shl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_shl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_shr8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_shr16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_sar8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_sar16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_rotl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); +void tcg_gen_vec_rotl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); + +/* 32-bit vector operations. */ +void tcg_gen_vec_add8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void tcg_gen_vec_add16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); + +void tcg_gen_vec_sub8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void tcg_gen_vec_sub16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); + +void tcg_gen_vec_shl8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_shl16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_shr8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_shr16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_sar8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); + +#endif diff --git a/include/tcg/tcg-op-gvec.h b/include/tcg/tcg-op-gvec.h index a8183bfeab..b0a81ad4bf 100644 --- a/include/tcg/tcg-op-gvec.h +++ b/include/tcg/tcg-op-gvec.h @@ -1,447 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Generic vector operation expansion + * Target dependent generic vector operation expansion * * Copyright (c) 2018 Linaro - * - * 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.1 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 TCG_TCG_OP_GVEC_H #define TCG_TCG_OP_GVEC_H -/* - * "Generic" vectors. All operands are given as offsets from ENV, - * and therefore cannot also be allocated via tcg_global_mem_new_*. - * OPRSZ is the byte size of the vector upon which the operation is performed. - * MAXSZ is the byte size of the full vector; bytes beyond OPSZ are cleared. - * - * All sizes must be 8 or any multiple of 16. - * When OPRSZ is 8, the alignment may be 8, otherwise must be 16. - * Operands may completely, but not partially, overlap. - */ +#include "tcg/tcg-op-gvec-common.h" -/* Expand a call to a gvec-style helper, with pointers to two vector - operands, and a descriptor (see tcg-gvec-desc.h). */ -typedef void gen_helper_gvec_2(TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_2 *fn); - -/* Similarly, passing an extra data value. */ -typedef void gen_helper_gvec_2i(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); -void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_2i *fn); - -/* Similarly, passing an extra pointer (e.g. env or float_status). */ -typedef void gen_helper_gvec_2_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_2_ptr(uint32_t dofs, uint32_t aofs, - TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, - int32_t data, gen_helper_gvec_2_ptr *fn); - -/* Similarly, with three vector operands. */ -typedef void gen_helper_gvec_3(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_3 *fn); - -/* Similarly, with four vector operands. */ -typedef void gen_helper_gvec_4(TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t oprsz, uint32_t maxsz, - int32_t data, gen_helper_gvec_4 *fn); - -/* Similarly, with five vector operands. */ -typedef void gen_helper_gvec_5(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_5_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t xofs, uint32_t oprsz, - uint32_t maxsz, int32_t data, gen_helper_gvec_5 *fn); - -typedef void gen_helper_gvec_3_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_3_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, - TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, - int32_t data, gen_helper_gvec_3_ptr *fn); - -typedef void gen_helper_gvec_4_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, TCGv_ptr ptr, uint32_t oprsz, - uint32_t maxsz, int32_t data, - gen_helper_gvec_4_ptr *fn); - -typedef void gen_helper_gvec_5_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_5_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t eofs, TCGv_ptr ptr, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_5_ptr *fn); - -/* Expand a gvec operation. Either inline or out-of-line depending on - the actual vector size and the operations supported by the host. */ -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_2 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - int32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 2nd source operand. */ - bool load_dest; -} GVecGen2; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, int64_t); - void (*fni4)(TCGv_i32, TCGv_i32, int32_t); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, int64_t); - /* Expand out-of-line helper w/descriptor, data in descriptor. */ - gen_helper_gvec_2 *fno; - /* Expand out-of-line helper w/descriptor, data as argument. */ - gen_helper_gvec_2i *fnoi; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 3rd source operand. */ - bool load_dest; -} GVecGen2i; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_2i *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - uint32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load scalar as 1st source operand. */ - bool scalar_first; -} GVecGen2s; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_3 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - int32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 3rd source operand. */ - bool load_dest; -} GVecGen3; - -typedef struct { - /* - * Expand inline as a 64-bit or 32-bit integer. Only one of these will be - * non-NULL. - */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); - /* Expand out-of-line helper w/descriptor, data in descriptor. */ - gen_helper_gvec_3 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 3rd source operand. */ - bool load_dest; -} GVecGen3i; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_4 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - int32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Write aofs as a 2nd dest operand. */ - bool write_aofs; -} GVecGen4; - -typedef struct { - /* - * Expand inline as a 64-bit or 32-bit integer. Only one of these will be - * non-NULL. - */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, int64_t); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, int32_t); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); - /* Expand out-of-line helper w/descriptor, data in descriptor. */ - gen_helper_gvec_4 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; -} GVecGen4i; - -void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); -void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, int64_t c, const GVecGen2i *); -void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, TCGv_i64 c, const GVecGen2s *); -void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); -void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, int64_t c, - const GVecGen3i *); -void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen4 *); -void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, - uint32_t oprsz, uint32_t maxsz, int64_t c, - const GVecGen4i *); - -/* Expand a specific vector operation. */ - -void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_abs(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_mul(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_addi(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_muli(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_adds(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_subs(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_muls(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); - -/* Saturated arithmetic. */ -void tcg_gen_gvec_ssadd(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sssub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -/* Min/max. */ -void tcg_gen_gvec_smin(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_umin(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_smax(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_and(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_xor(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_nand(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_nor(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_eqv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_andi(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_xori(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_ori(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_ands(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_andcs(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_xors(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_ors(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t s, uint32_t m); -void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t s, - uint32_t m, uint64_t imm); -void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t s, - uint32_t m, TCGv_i32); -void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t s, - uint32_t m, TCGv_i64); - -#if TARGET_LONG_BITS == 64 -# define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i64 -#else -# define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i32 +#ifndef TARGET_LONG_BITS +#error must include QEMU headers #endif -void tcg_gen_gvec_shli(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_shri(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sari(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotli(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotri(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_shls(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_shrs(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sars(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotls(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotrs(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); - -/* - * Perform vector shift by vector element, modulo the element size. - * E.g. D[i] = A[i] << (B[i] % (8 << vece)). - */ -void tcg_gen_gvec_shlv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_shrv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sarv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotlv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotrv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, - uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz); - -/* - * Perform vector bit select: d = (b & a) | (c & ~a). - */ -void tcg_gen_gvec_bitsel(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t cofs, - uint32_t oprsz, uint32_t maxsz); - -/* - * 64-bit vector operations. Use these when the register has been allocated - * with tcg_global_mem_new_i64, and so we cannot also address it via pointer. - * OPRSZ = MAXSZ = 8. - */ - -void tcg_gen_vec_neg8_i64(TCGv_i64 d, TCGv_i64 a); -void tcg_gen_vec_neg16_i64(TCGv_i64 d, TCGv_i64 a); -void tcg_gen_vec_neg32_i64(TCGv_i64 d, TCGv_i64 a); - -void tcg_gen_vec_add8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_add16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); - -void tcg_gen_vec_sub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_sub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); - -void tcg_gen_vec_shl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_shl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_shr8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_shr16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_sar8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_sar16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_rotl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); -void tcg_gen_vec_rotl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); - -/* 32-bit vector operations. */ -void tcg_gen_vec_add8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); -void tcg_gen_vec_add16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); - -void tcg_gen_vec_sub8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); -void tcg_gen_vec_sub16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); - -void tcg_gen_vec_shl8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_shl16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_shr8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_shr16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_sar8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); - #if TARGET_LONG_BITS == 64 +#define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i64 #define tcg_gen_vec_add8_tl tcg_gen_vec_add8_i64 #define tcg_gen_vec_sub8_tl tcg_gen_vec_sub8_i64 #define tcg_gen_vec_add16_tl tcg_gen_vec_add16_i64 @@ -454,8 +28,8 @@ void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); #define tcg_gen_vec_shl16i_tl tcg_gen_vec_shl16i_i64 #define tcg_gen_vec_shr16i_tl tcg_gen_vec_shr16i_i64 #define tcg_gen_vec_sar16i_tl tcg_gen_vec_sar16i_i64 - -#else +#elif TARGET_LONG_BITS == 32 +#define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i32 #define tcg_gen_vec_add8_tl tcg_gen_vec_add8_i32 #define tcg_gen_vec_sub8_tl tcg_gen_vec_sub8_i32 #define tcg_gen_vec_add16_tl tcg_gen_vec_add16_i32 @@ -468,6 +42,8 @@ void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); #define tcg_gen_vec_shl16i_tl tcg_gen_vec_shl16i_i32 #define tcg_gen_vec_shr16i_tl tcg_gen_vec_shr16i_i32 #define tcg_gen_vec_sar16i_tl tcg_gen_vec_sar16i_i32 +#else +# error #endif #endif diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 7a9599e49e..95a588d6d2 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -21,7 +21,7 @@ #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op-common.h" -#include "tcg/tcg-op-gvec.h" +#include "tcg/tcg-op-gvec-common.h" #include "tcg/tcg-gvec-desc.h" #define MAX_UNROLL 4 From 0a18945d037d9e7fd20a6c7fbc2226798a853323 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 14:24:08 -0700 Subject: [PATCH 100/412] tcg: Remove NO_CPU_IO_DEFS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From this remove, it's no longer clear what this is attempting to protect. The last time a use of this define was added to the source tree, as opposed to merely moved around, was 2008. There have been many cleanups since that time and this is no longer required for the build to succeed. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 1 - target/ppc/cpu.h | 2 -- target/sparc/cpu.h | 2 -- tcg/tcg.c | 6 ------ 4 files changed, 11 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 200de2793c..c4d081f5ad 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" -#define NO_CPU_IO_DEFS #include "trace.h" #include "disas/disas.h" #include "exec/exec-all.h" diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0f9f2e1a0c..10c4ffa148 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1394,7 +1394,6 @@ void ppc_store_msr(CPUPPCState *env, target_ulong value); void ppc_cpu_list(void); /* Time-base and decrementer management */ -#ifndef NO_CPU_IO_DEFS uint64_t cpu_ppc_load_tbl(CPUPPCState *env); uint32_t cpu_ppc_load_tbu(CPUPPCState *env); void cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value); @@ -1435,7 +1434,6 @@ int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif -#endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit, diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index fb98843dad..3d090e8278 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -581,7 +581,6 @@ G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, uintptr_t retaddr); G_NORETURN void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t); -#ifndef NO_CPU_IO_DEFS /* cpu_init.c */ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); void sparc_cpu_list(void); @@ -637,7 +636,6 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb, return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK); } -#endif #endif /* cpu-exec.c */ diff --git a/tcg/tcg.c b/tcg/tcg.c index a339e3e3d8..41186f540f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -34,12 +34,6 @@ #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" #include "qemu/timer.h" - -/* Note: the long term plan is to reduce the dependencies on the QEMU - CPU definitions. Currently they are used for qemu_ld/st - instructions */ -#define NO_CPU_IO_DEFS - #include "exec/exec-all.h" #include "exec/tlb-common.h" #include "tcg/tcg-op-common.h" From 087e2341fbf064b4de47b964670110f4d4642cd3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 14:35:46 -0700 Subject: [PATCH 101/412] exec-all: Widen tb_page_addr_t for user-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a step toward making TranslationBlock agnostic to the address size of the guest. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 3b1b57f6ad..ec0902c532 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -31,8 +31,8 @@ addresses in userspace mode. Define tb_page_addr_t to be an appropriate type. */ #if defined(CONFIG_USER_ONLY) -typedef abi_ulong tb_page_addr_t; -#define TB_PAGE_ADDR_FMT TARGET_ABI_FMT_lx +typedef vaddr tb_page_addr_t; +#define TB_PAGE_ADDR_FMT "%" VADDR_PRIx #else typedef ram_addr_t tb_page_addr_t; #define TB_PAGE_ADDR_FMT RAM_ADDR_FMT From 85314e13ad724247fbd74ff13555bff1cbda8356 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 15:28:18 -0700 Subject: [PATCH 102/412] exec-all: Widen TranslationBlock pc and cs_base to 64-bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes TranslationBlock agnostic to the address size of the guest. Use vaddr for pc, since that's always a virtual address. Use uint64_t for cs_base, since usage varies between guests. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 +- include/exec/exec-all.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 60ca9e229e..1cf4f1fa22 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -297,7 +297,7 @@ static void log_cpu_exec(target_ulong pc, CPUState *cpu, { if (qemu_log_in_addr_range(pc)) { qemu_log_mask(CPU_LOG_EXEC, - "Trace %d: %p [" TARGET_FMT_lx + "Trace %d: %p [%08" PRIx64 "/" TARGET_FMT_lx "/%08x/%08x] %s\n", cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc, tb->flags, tb->cflags, lookup_symbol(pc)); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index ec0902c532..dec17b1e62 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -516,7 +516,7 @@ struct TranslationBlock { * Unwind information is taken as offsets from the page, to be * deposited into the "current" PC. */ - target_ulong pc; + vaddr pc; /* * Target-specific data associated with the TranslationBlock, e.g.: @@ -525,7 +525,7 @@ struct TranslationBlock { * s390x: instruction data for EXECUTE, * sparc: the next pc of the instruction queue (for delay slots). */ - target_ulong cs_base; + uint64_t cs_base; uint32_t flags; /* flags defining in which context the code was generated */ uint32_t cflags; /* compile flags */ From bdbb9d6999dbf7f6c50828f58e81c6fa299041be Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 15:47:00 -0700 Subject: [PATCH 103/412] tcg: Spit out exec/translation-block.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is all that is required by tcg/ from exec-all.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 132 +-------------------------- include/exec/translation-block.h | 149 +++++++++++++++++++++++++++++++ tcg/tcg-op-ldst.c | 2 +- 3 files changed, 151 insertions(+), 132 deletions(-) create mode 100644 include/exec/translation-block.h diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index dec17b1e62..f01c7d57e8 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -24,20 +24,9 @@ #ifdef CONFIG_TCG #include "exec/cpu_ldst.h" #endif -#include "qemu/interval-tree.h" +#include "exec/translation-block.h" #include "qemu/clang-tsa.h" -/* Page tracking code uses ram addresses in system mode, and virtual - addresses in userspace mode. Define tb_page_addr_t to be an appropriate - type. */ -#if defined(CONFIG_USER_ONLY) -typedef vaddr tb_page_addr_t; -#define TB_PAGE_ADDR_FMT "%" VADDR_PRIx -#else -typedef ram_addr_t tb_page_addr_t; -#define TB_PAGE_ADDR_FMT RAM_ADDR_FMT -#endif - /** * cpu_unwind_state_data: * @cpu: the cpu context @@ -478,8 +467,6 @@ int probe_access_full(CPUArchState *env, target_ulong addr, int size, CPUTLBEntryFull **pfull, uintptr_t retaddr); #endif -#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ - /* Estimated block size for TB allocation. */ /* ??? The following is based on a 2015 survey of x86_64 host output. Better would seem to be some sort of dynamically sized TB array, @@ -490,123 +477,6 @@ int probe_access_full(CPUArchState *env, target_ulong addr, int size, #define CODE_GEN_AVG_BLOCK_SIZE 150 #endif -/* - * Translation Cache-related fields of a TB. - * This struct exists just for convenience; we keep track of TB's in a binary - * search tree, and the only fields needed to compare TB's in the tree are - * @ptr and @size. - * Note: the address of search data can be obtained by adding @size to @ptr. - */ -struct tb_tc { - const void *ptr; /* pointer to the translated code */ - size_t size; -}; - -struct TranslationBlock { - /* - * Guest PC corresponding to this block. This must be the true - * virtual address. Therefore e.g. x86 stores EIP + CS_BASE, and - * targets like Arm, MIPS, HP-PA, which reuse low bits for ISA or - * privilege, must store those bits elsewhere. - * - * If CF_PCREL, the opcodes for the TranslationBlock are written - * such that the TB is associated only with the physical page and - * may be run in any virtual address context. In this case, PC - * must always be taken from ENV in a target-specific manner. - * Unwind information is taken as offsets from the page, to be - * deposited into the "current" PC. - */ - vaddr pc; - - /* - * Target-specific data associated with the TranslationBlock, e.g.: - * x86: the original user, the Code Segment virtual base, - * arm: an extension of tb->flags, - * s390x: instruction data for EXECUTE, - * sparc: the next pc of the instruction queue (for delay slots). - */ - uint64_t cs_base; - - uint32_t flags; /* flags defining in which context the code was generated */ - uint32_t cflags; /* compile flags */ - -/* Note that TCG_MAX_INSNS is 512; we validate this match elsewhere. */ -#define CF_COUNT_MASK 0x000001ff -#define CF_NO_GOTO_TB 0x00000200 /* Do not chain with goto_tb */ -#define CF_NO_GOTO_PTR 0x00000400 /* Do not chain with goto_ptr */ -#define CF_SINGLE_STEP 0x00000800 /* gdbstub single-step in effect */ -#define CF_LAST_IO 0x00008000 /* Last insn may be an IO access. */ -#define CF_MEMI_ONLY 0x00010000 /* Only instrument memory ops */ -#define CF_USE_ICOUNT 0x00020000 -#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ -#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ -#define CF_NOIRQ 0x00100000 /* Generate an uninterruptible TB */ -#define CF_PCREL 0x00200000 /* Opcodes in TB are PC-relative */ -#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ -#define CF_CLUSTER_SHIFT 24 - - /* - * Above fields used for comparing - */ - - /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ - uint16_t size; - uint16_t icount; - - struct tb_tc tc; - - /* - * Track tb_page_addr_t intervals that intersect this TB. - * For user-only, the virtual addresses are always contiguous, - * and we use a unified interval tree. For system, we use a - * linked list headed in each PageDesc. Within the list, the lsb - * of the previous pointer tells the index of page_next[], and the - * list is protected by the PageDesc lock(s). - */ -#ifdef CONFIG_USER_ONLY - IntervalTreeNode itree; -#else - uintptr_t page_next[2]; - tb_page_addr_t page_addr[2]; -#endif - - /* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */ - QemuSpin jmp_lock; - - /* The following data are used to directly call another TB from - * the code of this one. This can be done either by emitting direct or - * indirect native jump instructions. These jumps are reset so that the TB - * just continues its execution. The TB can be linked to another one by - * setting one of the jump targets (or patching the jump instruction). Only - * two of such jumps are supported. - */ -#define TB_JMP_OFFSET_INVALID 0xffff /* indicates no jump generated */ - uint16_t jmp_reset_offset[2]; /* offset of original jump target */ - uint16_t jmp_insn_offset[2]; /* offset of direct jump insn */ - uintptr_t jmp_target_addr[2]; /* target address */ - - /* - * Each TB has a NULL-terminated list (jmp_list_head) of incoming jumps. - * Each TB can have two outgoing jumps, and therefore can participate - * in two lists. The list entries are kept in jmp_list_next[2]. The least - * significant bit (LSB) of the pointers in these lists is used to encode - * which of the two list entries is to be used in the pointed TB. - * - * List traversals are protected by jmp_lock. The destination TB of each - * outgoing jump is kept in jmp_dest[] so that the appropriate jmp_lock - * can be acquired from any origin TB. - * - * jmp_dest[] are tagged pointers as well. The LSB is set when the TB is - * being invalidated, so that no further outgoing jumps from it can be set. - * - * jmp_lock also protects the CF_INVALID cflag; a jump must not be chained - * to a destination TB that has CF_INVALID set. - */ - uintptr_t jmp_list_head; - uintptr_t jmp_list_next[2]; - uintptr_t jmp_dest[2]; -}; - /* Hide the qatomic_read to make code a little easier on the eyes */ static inline uint32_t tb_cflags(const TranslationBlock *tb) { diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h new file mode 100644 index 0000000000..5119924927 --- /dev/null +++ b/include/exec/translation-block.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Definition of TranslationBlock. + * Copyright (c) 2003 Fabrice Bellard + */ + +#ifndef EXEC_TRANSLATION_BLOCK_H +#define EXEC_TRANSLATION_BLOCK_H + +#include "qemu/atomic.h" +#include "qemu/thread.h" +#include "qemu/interval-tree.h" +#include "exec/cpu-common.h" +#include "exec/target_page.h" + +/* + * Page tracking code uses ram addresses in system mode, and virtual + * addresses in userspace mode. Define tb_page_addr_t to be an + * appropriate type. + */ +#if defined(CONFIG_USER_ONLY) +typedef vaddr tb_page_addr_t; +#define TB_PAGE_ADDR_FMT "%" VADDR_PRIx +#else +typedef ram_addr_t tb_page_addr_t; +#define TB_PAGE_ADDR_FMT RAM_ADDR_FMT +#endif + +/* + * Translation Cache-related fields of a TB. + * This struct exists just for convenience; we keep track of TB's in a binary + * search tree, and the only fields needed to compare TB's in the tree are + * @ptr and @size. + * Note: the address of search data can be obtained by adding @size to @ptr. + */ +struct tb_tc { + const void *ptr; /* pointer to the translated code */ + size_t size; +}; + +struct TranslationBlock { + /* + * Guest PC corresponding to this block. This must be the true + * virtual address. Therefore e.g. x86 stores EIP + CS_BASE, and + * targets like Arm, MIPS, HP-PA, which reuse low bits for ISA or + * privilege, must store those bits elsewhere. + * + * If CF_PCREL, the opcodes for the TranslationBlock are written + * such that the TB is associated only with the physical page and + * may be run in any virtual address context. In this case, PC + * must always be taken from ENV in a target-specific manner. + * Unwind information is taken as offsets from the page, to be + * deposited into the "current" PC. + */ + vaddr pc; + + /* + * Target-specific data associated with the TranslationBlock, e.g.: + * x86: the original user, the Code Segment virtual base, + * arm: an extension of tb->flags, + * s390x: instruction data for EXECUTE, + * sparc: the next pc of the instruction queue (for delay slots). + */ + uint64_t cs_base; + + uint32_t flags; /* flags defining in which context the code was generated */ + uint32_t cflags; /* compile flags */ + +/* Note that TCG_MAX_INSNS is 512; we validate this match elsewhere. */ +#define CF_COUNT_MASK 0x000001ff +#define CF_NO_GOTO_TB 0x00000200 /* Do not chain with goto_tb */ +#define CF_NO_GOTO_PTR 0x00000400 /* Do not chain with goto_ptr */ +#define CF_SINGLE_STEP 0x00000800 /* gdbstub single-step in effect */ +#define CF_LAST_IO 0x00008000 /* Last insn may be an IO access. */ +#define CF_MEMI_ONLY 0x00010000 /* Only instrument memory ops */ +#define CF_USE_ICOUNT 0x00020000 +#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ +#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ +#define CF_NOIRQ 0x00100000 /* Generate an uninterruptible TB */ +#define CF_PCREL 0x00200000 /* Opcodes in TB are PC-relative */ +#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ +#define CF_CLUSTER_SHIFT 24 + + /* + * Above fields used for comparing + */ + + /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ + uint16_t size; + uint16_t icount; + + struct tb_tc tc; + + /* + * Track tb_page_addr_t intervals that intersect this TB. + * For user-only, the virtual addresses are always contiguous, + * and we use a unified interval tree. For system, we use a + * linked list headed in each PageDesc. Within the list, the lsb + * of the previous pointer tells the index of page_next[], and the + * list is protected by the PageDesc lock(s). + */ +#ifdef CONFIG_USER_ONLY + IntervalTreeNode itree; +#else + uintptr_t page_next[2]; + tb_page_addr_t page_addr[2]; +#endif + + /* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */ + QemuSpin jmp_lock; + + /* The following data are used to directly call another TB from + * the code of this one. This can be done either by emitting direct or + * indirect native jump instructions. These jumps are reset so that the TB + * just continues its execution. The TB can be linked to another one by + * setting one of the jump targets (or patching the jump instruction). Only + * two of such jumps are supported. + */ +#define TB_JMP_OFFSET_INVALID 0xffff /* indicates no jump generated */ + uint16_t jmp_reset_offset[2]; /* offset of original jump target */ + uint16_t jmp_insn_offset[2]; /* offset of direct jump insn */ + uintptr_t jmp_target_addr[2]; /* target address */ + + /* + * Each TB has a NULL-terminated list (jmp_list_head) of incoming jumps. + * Each TB can have two outgoing jumps, and therefore can participate + * in two lists. The list entries are kept in jmp_list_next[2]. The least + * significant bit (LSB) of the pointers in these lists is used to encode + * which of the two list entries is to be used in the pointed TB. + * + * List traversals are protected by jmp_lock. The destination TB of each + * outgoing jump is kept in jmp_dest[] so that the appropriate jmp_lock + * can be acquired from any origin TB. + * + * jmp_dest[] are tagged pointers as well. The LSB is set when the TB is + * being invalidated, so that no further outgoing jumps from it can be set. + * + * jmp_lock also protects the CF_INVALID cflag; a jump must not be chained + * to a destination TB that has CF_INVALID set. + */ + uintptr_t jmp_list_head; + uintptr_t jmp_list_next[2]; + uintptr_t jmp_dest[2]; +}; + +/* The alignment given to TranslationBlock during allocation. */ +#define CODE_GEN_ALIGN 16 + +#endif /* EXEC_TRANSLATION_BLOCK_H */ diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 46a5977b35..a4f51bfb6e 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -23,11 +23,11 @@ */ #include "qemu/osdep.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" +#include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" From 80c5813d9068b034eb5abf9366b72c8417bcd17d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 1 May 2023 08:08:27 +0100 Subject: [PATCH 104/412] include/exec: Remove CODE_GEN_AVG_BLOCK_SIZE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last use was removed with 2ac01d6dafab. Fixes: 2ac01d6dafab ("translate-all: use a binary search tree to track TBs in TBContext") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index f01c7d57e8..698943d58f 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -467,16 +467,6 @@ int probe_access_full(CPUArchState *env, target_ulong addr, int size, CPUTLBEntryFull **pfull, uintptr_t retaddr); #endif -/* Estimated block size for TB allocation. */ -/* ??? The following is based on a 2015 survey of x86_64 host output. - Better would seem to be some sort of dynamically sized TB array, - adapting to the block sizes actually being produced. */ -#if defined(CONFIG_SOFTMMU) -#define CODE_GEN_AVG_BLOCK_SIZE 400 -#else -#define CODE_GEN_AVG_BLOCK_SIZE 150 -#endif - /* Hide the qatomic_read to make code a little easier on the eyes */ static inline uint32_t tb_cflags(const TranslationBlock *tb) { From 56234233594d05b1092b3cb04de845aeffa27f4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 17:09:47 -0700 Subject: [PATCH 105/412] accel/tcg: Move most of gen-icount.h into translator.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only usage of gen_tb_start and gen_tb_end are here. Move the static icount_start_insn variable into a local within translator_loop. Simplify the two subroutines by passing in the existing local cflags variable. Leave only the declaration of gen_io_start in gen-icount.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 83 ++++++++++++++++++++++++++++++++++++++- include/exec/gen-icount.h | 79 +------------------------------------ 2 files changed, 82 insertions(+), 80 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 6120ef2a92..b0d0015c70 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -18,6 +18,84 @@ #include "exec/plugin-gen.h" #include "exec/replay-core.h" + +void gen_io_start(void) +{ + tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, + offsetof(ArchCPU, parent_obj.can_do_io) - + offsetof(ArchCPU, env)); +} + +static TCGOp *gen_tb_start(uint32_t cflags) +{ + TCGv_i32 count = tcg_temp_new_i32(); + TCGOp *icount_start_insn = NULL; + + tcg_gen_ld_i32(count, cpu_env, + offsetof(ArchCPU, neg.icount_decr.u32) - + offsetof(ArchCPU, env)); + + if (cflags & CF_USE_ICOUNT) { + /* + * We emit a sub with a dummy immediate argument. Keep the insn index + * of the sub so that we later (when we know the actual insn count) + * can update the argument with the actual insn count. + */ + tcg_gen_sub_i32(count, count, tcg_constant_i32(0)); + icount_start_insn = tcg_last_op(); + } + + /* + * Emit the check against icount_decr.u32 to see if we should exit + * unless we suppress the check with CF_NOIRQ. If we are using + * icount and have suppressed interruption the higher level code + * should have ensured we don't run more instructions than the + * budget. + */ + if (cflags & CF_NOIRQ) { + tcg_ctx->exitreq_label = NULL; + } else { + tcg_ctx->exitreq_label = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label); + } + + if (cflags & CF_USE_ICOUNT) { + tcg_gen_st16_i32(count, cpu_env, + offsetof(ArchCPU, neg.icount_decr.u16.low) - + offsetof(ArchCPU, env)); + /* + * cpu->can_do_io is cleared automatically here at the beginning of + * each translation block. The cost is minimal and only paid for + * -icount, plus it would be very easy to forget doing it in the + * translator. Doing it here means we don't need a gen_io_end() to + * go with gen_io_start(). + */ + tcg_gen_st_i32(tcg_constant_i32(0), cpu_env, + offsetof(ArchCPU, parent_obj.can_do_io) - + offsetof(ArchCPU, env)); + } + + return icount_start_insn; +} + +static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags, + TCGOp *icount_start_insn, int num_insns) +{ + if (cflags & CF_USE_ICOUNT) { + /* + * Update the num_insn immediate parameter now that we know + * the actual insn count. + */ + tcg_set_insn_param(icount_start_insn, 2, + tcgv_i32_arg(tcg_constant_i32(num_insns))); + } + + if (tcg_ctx->exitreq_label) { + gen_set_label(tcg_ctx->exitreq_label); + tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); + } +} + bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) { /* Suppress goto_tb if requested. */ @@ -34,6 +112,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, const TranslatorOps *ops, DisasContextBase *db) { uint32_t cflags = tb_cflags(tb); + TCGOp *icount_start_insn; bool plugin_enabled; /* Initialize DisasContext */ @@ -55,7 +134,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ /* Start translating. */ - gen_tb_start(db->tb); + icount_start_insn = gen_tb_start(cflags); ops->tb_start(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ @@ -112,7 +191,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, /* Emit code to exit the TB, as indicated by db->is_jmp. */ ops->tb_stop(db, cpu); - gen_tb_end(db->tb, db->num_insns); + gen_tb_end(tb, cflags, icount_start_insn, db->num_insns); if (plugin_enabled) { plugin_gen_tb_end(cpu); diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index f6de79a6b4..6006af4c06 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -1,83 +1,6 @@ #ifndef GEN_ICOUNT_H #define GEN_ICOUNT_H -#include "exec/exec-all.h" - -/* Helpers for instruction counting code generation. */ - -static TCGOp *icount_start_insn; - -static inline void gen_io_start(void) -{ - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, - offsetof(ArchCPU, parent_obj.can_do_io) - - offsetof(ArchCPU, env)); -} - -static inline void gen_tb_start(const TranslationBlock *tb) -{ - TCGv_i32 count = tcg_temp_new_i32(); - - tcg_gen_ld_i32(count, cpu_env, - offsetof(ArchCPU, neg.icount_decr.u32) - - offsetof(ArchCPU, env)); - - if (tb_cflags(tb) & CF_USE_ICOUNT) { - /* - * We emit a sub with a dummy immediate argument. Keep the insn index - * of the sub so that we later (when we know the actual insn count) - * can update the argument with the actual insn count. - */ - tcg_gen_sub_i32(count, count, tcg_constant_i32(0)); - icount_start_insn = tcg_last_op(); - } - - /* - * Emit the check against icount_decr.u32 to see if we should exit - * unless we suppress the check with CF_NOIRQ. If we are using - * icount and have suppressed interruption the higher level code - * should have ensured we don't run more instructions than the - * budget. - */ - if (tb_cflags(tb) & CF_NOIRQ) { - tcg_ctx->exitreq_label = NULL; - } else { - tcg_ctx->exitreq_label = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label); - } - - if (tb_cflags(tb) & CF_USE_ICOUNT) { - tcg_gen_st16_i32(count, cpu_env, - offsetof(ArchCPU, neg.icount_decr.u16.low) - - offsetof(ArchCPU, env)); - /* - * cpu->can_do_io is cleared automatically here at the beginning of - * each translation block. The cost is minimal and only paid for - * -icount, plus it would be very easy to forget doing it in the - * translator. Doing it here means we don't need a gen_io_end() to - * go with gen_io_start(). - */ - tcg_gen_st_i32(tcg_constant_i32(0), cpu_env, - offsetof(ArchCPU, parent_obj.can_do_io) - - offsetof(ArchCPU, env)); - } -} - -static inline void gen_tb_end(const TranslationBlock *tb, int num_insns) -{ - if (tb_cflags(tb) & CF_USE_ICOUNT) { - /* - * Update the num_insn immediate parameter now that we know - * the actual insn count. - */ - tcg_set_insn_param(icount_start_insn, 2, - tcgv_i32_arg(tcg_constant_i32(num_insns))); - } - - if (tcg_ctx->exitreq_label) { - gen_set_label(tcg_ctx->exitreq_label); - tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); - } -} +void gen_io_start(void); #endif From dfd1b81274140c5f511d549f7b3ec7675a6597f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 22 May 2023 23:08:01 -0700 Subject: [PATCH 106/412] accel/tcg: Introduce translator_io_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New wrapper around gen_io_start which takes care of the USE_ICOUNT check, as well as marking the DisasContext to end the TB. Remove exec/gen-icount.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- MAINTAINERS | 1 - accel/tcg/translator.c | 27 ++++++- include/exec/gen-icount.h | 6 -- include/exec/translator.h | 10 +++ target/alpha/translate.c | 15 +--- target/arm/cpregs.h | 4 +- target/arm/tcg/translate-a64.c | 23 +++--- target/arm/tcg/translate-mve.c | 1 - target/arm/tcg/translate-neon.c | 1 - target/arm/tcg/translate-vfp.c | 4 +- target/arm/tcg/translate.c | 20 ++--- target/avr/translate.c | 1 - target/cris/translate.c | 2 - target/hppa/translate.c | 5 +- target/i386/tcg/translate.c | 52 +++---------- target/loongarch/insn_trans/trans_extra.c.inc | 4 +- .../insn_trans/trans_privileged.c.inc | 4 +- target/loongarch/translate.c | 2 - target/m68k/translate.c | 2 - target/microblaze/translate.c | 2 - target/mips/tcg/translate.c | 29 +++---- target/nios2/translate.c | 1 - target/openrisc/translate.c | 9 +-- target/ppc/translate.c | 13 +--- .../riscv/insn_trans/trans_privileged.c.inc | 8 +- target/riscv/insn_trans/trans_rvi.c.inc | 24 ++---- target/riscv/translate.c | 2 - target/rx/translate.c | 2 - target/s390x/tcg/translate.c | 6 +- target/sh4/translate.c | 2 - target/sparc/translate.c | 75 +++++-------------- target/tricore/translate.c | 2 - target/xtensa/translate.c | 27 ++----- 33 files changed, 117 insertions(+), 269 deletions(-) delete mode 100644 include/exec/gen-icount.h diff --git a/MAINTAINERS b/MAINTAINERS index 2366f64d3d..55668d6336 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2867,7 +2867,6 @@ F: ui/cocoa.m Main loop M: Paolo Bonzini S: Maintained -F: include/exec/gen-icount.h F: include/qemu/main-loop.h F: include/sysemu/runstate.h F: include/sysemu/runstate-action.h diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index b0d0015c70..7a130e706e 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -12,20 +12,43 @@ #include "tcg/tcg.h" #include "tcg/tcg-op.h" #include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "exec/log.h" #include "exec/translator.h" #include "exec/plugin-gen.h" #include "exec/replay-core.h" -void gen_io_start(void) +static void gen_io_start(void) { tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, offsetof(ArchCPU, parent_obj.can_do_io) - offsetof(ArchCPU, env)); } +bool translator_io_start(DisasContextBase *db) +{ + uint32_t cflags = tb_cflags(db->tb); + + if (!(cflags & CF_USE_ICOUNT)) { + return false; + } + if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { + /* Already started in translator_loop. */ + return true; + } + + gen_io_start(); + + /* + * Ensure that this instruction will be the last in the TB. + * The target may override this to something more forceful. + */ + if (db->is_jmp == DISAS_NEXT) { + db->is_jmp = DISAS_TOO_MANY; + } + return true; +} + static TCGOp *gen_tb_start(uint32_t cflags) { TCGv_i32 count = tcg_temp_new_i32(); diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h deleted file mode 100644 index 6006af4c06..0000000000 --- a/include/exec/gen-icount.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef GEN_ICOUNT_H -#define GEN_ICOUNT_H - -void gen_io_start(void); - -#endif diff --git a/include/exec/translator.h b/include/exec/translator.h index 797fef7515..c1a1203789 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -160,6 +160,16 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, */ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest); +/** + * translator_io_start + * @db: Disassembly context + * + * If icount is enabled, set cpu->can_to_io, adjust db->is_jmp to + * DISAS_TOO_MANY if it is still DISAS_NEXT, and return true. + * Otherwise return false. + */ +bool translator_io_start(DisasContextBase *db); + /* * Translator Load Functions * diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 545e5743c3..1f7dd078d8 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -96,8 +96,6 @@ static TCGv cpu_lock_value; static TCGv cpu_pal_ir[31]; #endif -#include "exec/gen-icount.h" - void alpha_translate_init(void) { #define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUAlphaState, V) } @@ -1236,8 +1234,7 @@ static DisasJumpType gen_mfpr(DisasContext *ctx, TCGv va, int regno) case 249: /* VMTIME */ helper = gen_helper_get_vmtime; do_helper: - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&ctx->base)) { helper(va); return DISAS_PC_STALE; } else { @@ -1298,8 +1295,7 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) case 251: /* ALARM */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&ctx->base)) { ret = DISAS_PC_STALE; } gen_helper_set_alarm(cpu_env, vb); @@ -2335,13 +2331,10 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0xC000: /* RPCC */ va = dest_gpr(ctx, ra); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - gen_helper_load_pcc(va, cpu_env); + if (translator_io_start(&ctx->base)) { ret = DISAS_PC_STALE; - } else { - gen_helper_load_pcc(va, cpu_env); } + gen_helper_load_pcc(va, cpu_env); break; case 0xE000: /* RC */ diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index b04d344a9f..14785686f6 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -67,8 +67,8 @@ enum { ARM_CP_ALIAS = 1 << 8, /* * Flag: Register does I/O and therefore its accesses need to be marked - * with gen_io_start() and also end the TB. In particular, registers which - * implement clocks or timers require this. + * with translator_io_start() and also end the TB. In particular, + * registers which implement clocks or timers require this. */ ARM_CP_IO = 1 << 9, /* diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bc0cb98955..8d45dbf8fc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -28,7 +28,6 @@ #include "internals.h" #include "qemu/host-utils.h" #include "semihosting/semihost.h" -#include "exec/gen-icount.h" #include "exec/log.h" #include "cpregs.h" #include "translate-a64.h" @@ -1552,9 +1551,7 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a) tcg_gen_ld_i64(dst, cpu_env, offsetof(CPUARMState, elr_el[s->current_el])); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_helper_exception_return(cpu_env, dst); /* Must exit loop to check un-masked IRQs */ @@ -1582,9 +1579,8 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a) offsetof(CPUARMState, elr_el[s->current_el])); dst = auth_branch_target(s, dst, cpu_X[31], !a->m); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + + translator_io_start(&s->base); gen_helper_exception_return(cpu_env, dst); /* Must exit loop to check un-masked IRQs */ @@ -2044,6 +2040,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, uint32_t key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2); const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); + bool need_exit_tb = false; TCGv_ptr tcg_ri = NULL; TCGv_i64 tcg_rt; @@ -2171,8 +2168,9 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, return; } - if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); + if (ri->type & ARM_CP_IO) { + /* I/O operations must end the TB here (whether read or write) */ + need_exit_tb = translator_io_start(&s->base); } tcg_rt = cpu_reg(s, rt); @@ -2202,10 +2200,6 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } } - if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - /* I/O operations must end the TB here (whether read or write) */ - s->base.is_jmp = DISAS_UPDATE_EXIT; - } if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* * A write to any coprocessor regiser that ends a TB @@ -2217,6 +2211,9 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * but allow this to be suppressed by the register definition * (usually only necessary to work around guest bugs). */ + need_exit_tb = true; + } + if (need_exit_tb) { s->base.is_jmp = DISAS_UPDATE_EXIT; } } diff --git a/target/arm/tcg/translate-mve.c b/target/arm/tcg/translate-mve.c index 31fb2110f1..2ad3c40975 100644 --- a/target/arm/tcg/translate-mve.c +++ b/target/arm/tcg/translate-mve.c @@ -21,7 +21,6 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "translate.h" #include "translate-a32.h" diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index af8685a4ac..6fac577abd 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -24,7 +24,6 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "translate.h" #include "translate-a32.h" diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index dd782aacf4..95ac8d9db3 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -24,7 +24,6 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "translate.h" #include "translate-a32.h" @@ -117,9 +116,8 @@ static void gen_preserve_fp_state(DisasContext *s, bool skip_context_update) * so we must mark it as an IO operation for icount (and cause * this to be the last insn in the TB). */ - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + if (translator_io_start(&s->base)) { s->base.is_jmp = DISAS_UPDATE_EXIT; - gen_io_start(); } gen_helper_v7m_preserve_fp_state(cpu_env); /* diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 379f266256..7caf6d802d 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -34,7 +34,6 @@ #include "cpregs.h" #include "translate.h" #include "translate-a32.h" -#include "exec/gen-icount.h" #include "exec/helper-proto.h" #define HELPER_H "helper.h" @@ -2908,9 +2907,7 @@ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) * appropriately depending on the new Thumb bit, so it must * be called after storing the new PC. */ - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_helper_cpsr_write_eret(cpu_env, cpsr); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; @@ -4559,7 +4556,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, uint32_t key = ENCODE_CP_REG(cpnum, is64, s->ns, crn, crm, opc1, opc2); const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); TCGv_ptr tcg_ri = NULL; - bool need_exit_tb; + bool need_exit_tb = false; uint32_t syndrome; /* @@ -4704,8 +4701,9 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, g_assert_not_reached(); } - if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); + if (ri->type & ARM_CP_IO) { + /* I/O operations must end the TB here (whether read or write) */ + need_exit_tb = translator_io_start(&s->base); } if (isread) { @@ -4787,10 +4785,6 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } } - /* I/O operations must end the TB here (whether read or write) */ - need_exit_tb = ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && - (ri->type & ARM_CP_IO)); - if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* * A write to any coprocessor register that ends a TB @@ -8047,9 +8041,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) if (exc_return) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_helper_cpsr_write_eret(cpu_env, tmp); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; diff --git a/target/avr/translate.c b/target/avr/translate.c index 4fa40b568a..ef2edd7415 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -29,7 +29,6 @@ #include "exec/helper-gen.h" #include "exec/log.h" #include "exec/translator.h" -#include "exec/gen-icount.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" diff --git a/target/cris/translate.c b/target/cris/translate.c index 3c21826cc2..1445cd8bb5 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -88,8 +88,6 @@ static TCGv env_btaken; static TCGv env_btarget; static TCGv env_pc; -#include "exec/gen-icount.h" - /* This is the state at translation time. */ typedef struct DisasContext { DisasContextBase base; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 2c50fa72c3..d33813d173 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -364,8 +364,6 @@ static TCGv_reg cpu_psw_v; static TCGv_reg cpu_psw_cb; static TCGv_reg cpu_psw_cb_msb; -#include "exec/gen-icount.h" - void hppa_translate_init(void) { #define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUHPPAState, V) } @@ -2090,8 +2088,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) /* FIXME: Respect PSW_S bit. */ nullify_over(ctx); tmp = dest_gpr(ctx, rt); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&ctx->base)) { gen_helper_read_interval_timer(tmp); ctx->base.is_jmp = DISAS_IAQ_N_STALE; } else { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index d509105505..5cf14311a6 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -78,8 +78,6 @@ static TCGv cpu_seg_base[6]; static TCGv_i64 cpu_bndl[4]; static TCGv_i64 cpu_bndu[4]; -#include "exec/gen-icount.h" - typedef struct DisasContext { DisasContextBase base; @@ -3933,10 +3931,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) !(s->cpuid_ext_features & CPUID_EXT_RDRAND)) { goto illegal_op; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_helper_rdrand(s->T0, cpu_env); rm = (modrm & 7) | REX_B(s); gen_op_mov_reg_v(s, dflag, rm, s->T0); @@ -4974,10 +4969,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { gen_repz_ins(s, ot); } else { @@ -4992,10 +4984,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_STR_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { gen_repz_outs(s, ot); } else { @@ -5014,10 +5003,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_helper_in_func(ot, s->T1, s->tmp2_i32); gen_op_mov_reg_v(s, ot, R_EAX, s->T1); gen_bpt_io(s, s->tmp2_i32, ot); @@ -5030,10 +5016,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_op_mov_v_reg(s, ot, s->T1, R_EAX); tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); @@ -5047,10 +5030,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_helper_in_func(ot, s->T1, s->tmp2_i32); gen_op_mov_reg_v(s, ot, R_EAX, s->T1); gen_bpt_io(s, s->tmp2_i32, ot); @@ -5063,10 +5043,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_op_mov_v_reg(s, ot, s->T1, R_EAX); tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); @@ -5674,10 +5651,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) case 0x131: /* rdtsc */ gen_update_cc_op(s); gen_update_eip_cur(s); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_helper_rdtsc(cpu_env); break; case 0x133: /* rdpmc */ @@ -6133,10 +6107,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); gen_helper_rdtscp(cpu_env); break; @@ -6490,10 +6461,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } ot = (CODE64(s) ? MO_64 : MO_32); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&s->base); if (b & 2) { gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0 + reg); gen_op_mov_v_reg(s, ot, s->T0, rm); diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc index ad713cd61e..06f4de4515 100644 --- a/target/loongarch/insn_trans/trans_extra.c.inc +++ b/target/loongarch/insn_trans/trans_extra.c.inc @@ -39,9 +39,7 @@ static bool gen_rdtime(DisasContext *ctx, arg_rr *a, TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE); TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_rdtime_d(dst1, cpu_env); if (word) { tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32); diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc index 5a04352b01..02bca7ca23 100644 --- a/target/loongarch/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/insn_trans/trans_privileged.c.inc @@ -185,9 +185,7 @@ static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) if ((csr->flags & CSRFL_READONLY) && write) { return false; } - if ((csr->flags & CSRFL_IO) && - (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) { - gen_io_start(); + if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) { ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else if ((csr->flags & CSRFL_EXITTB) && write) { ctx->base.is_jmp = DISAS_EXIT_UPDATE; diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c index 67140ada56..1cf27a4611 100644 --- a/target/loongarch/translate.c +++ b/target/loongarch/translate.c @@ -24,8 +24,6 @@ TCGv cpu_gpr[32], cpu_pc; static TCGv cpu_lladdr, cpu_llval; -#include "exec/gen-icount.h" - #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" #undef HELPER_H diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 90ca51fb9e..551ef9e52a 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -65,8 +65,6 @@ static TCGv NULL_QREG; /* Used to distinguish stores from bad addressing modes. */ static TCGv store_dummy; -#include "exec/gen-icount.h" - void m68k_tcg_init(void) { char *p; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7a5d1066da..7e7f837c63 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -58,8 +58,6 @@ static TCGv_i32 cpu_iflags; static TCGv cpu_res_addr; static TCGv_i32 cpu_res_val; -#include "exec/gen-icount.h" - /* This is the state at translation time. */ typedef struct DisasContext { DisasContextBase base; diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index bff1859b86..312ed66989 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1215,8 +1215,6 @@ static TCGv_i32 hflags; TCGv_i32 fpu_fcr0, fpu_fcr31; TCGv_i64 fpu_f64[32]; -#include "exec/gen-icount.h" - static const char regnames_HI[][4] = { "HI0", "HI1", "HI2", "HI3", }; @@ -5670,9 +5668,8 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG09__COUNT: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); + gen_helper_mfc0_count(arg, cpu_env); /* * Break the TB to be able to take timer interrupts immediately @@ -6111,14 +6108,13 @@ cp0_unimplemented: static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *register_name = "invalid"; + bool icount; if (sel != 0) { check_insn(ctx, ISA_MIPS_R1); } - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + icount = translator_io_start(&ctx->base); switch (reg) { case CP0_REGISTER_00: @@ -6856,7 +6852,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("mtc0", register_name, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + if (icount) { /* * DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. @@ -7173,9 +7169,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG09__COUNT: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_mfc0_count(arg, cpu_env); /* * Break the TB to be able to take timer interrupts immediately @@ -7601,14 +7595,13 @@ cp0_unimplemented: static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *register_name = "invalid"; + bool icount; if (sel != 0) { check_insn(ctx, ISA_MIPS_R1); } - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + icount = translator_io_start(&ctx->base); switch (reg) { case CP0_REGISTER_00: @@ -8336,7 +8329,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("dmtc0", register_name, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + if (icount) { /* * DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. @@ -11147,9 +11140,7 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) gen_store_gpr(t0, rt); break; case 2: - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_rdhwr_cc(t0, cpu_env); gen_store_gpr(t0, rt); /* diff --git a/target/nios2/translate.c b/target/nios2/translate.c index 28c1d700e1..a365ad8293 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -32,7 +32,6 @@ #include "exec/cpu_ldst.h" #include "exec/translator.h" #include "qemu/qemu-print.h" -#include "exec/gen-icount.h" #include "semihosting/semihost.h" #define HELPER_H "helper.h" diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 06e6eae952..7760329e75 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -31,7 +31,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "exec/gen-icount.h" #include "exec/log.h" @@ -828,8 +827,7 @@ static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a) check_r0_write(dc, a->d); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&dc->base)) { if (dc->delayed_branch) { tcg_gen_mov_tl(cpu_pc, jmp_pc); tcg_gen_discard_tl(jmp_pc); @@ -848,9 +846,8 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) { TCGv spr = tcg_temp_new(); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); + /* * For SR, we will need to exit the TB to recognize the new * exception state. For NPC, in theory this counts as a branch diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 67d7ee0a70..519f66bb05 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -80,8 +80,6 @@ static TCGv cpu_reserve_val2; static TCGv cpu_fpscr; static TCGv_i32 cpu_access_type; -#include "exec/gen-icount.h" - void ppc_translate_init(void) { int i; @@ -300,16 +298,7 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, static void gen_icount_io_start(DisasContext *ctx) { - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - /* - * An I/O instruction must be last in the TB. - * Chain to the next TB, and let the code from gen_tb_start - * decide if we need to return to the main loop. - * Doing this first also allows this value to be overridden. - */ - ctx->base.is_jmp = DISAS_TOO_MANY; - } + translator_io_start(&ctx->base); } #if !defined(CONFIG_USER_ONLY) diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 7c2837194c..528baa1652 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -77,9 +77,7 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) #ifndef CONFIG_USER_ONLY if (has_ext(ctx, RVS)) { decode_save_opc(ctx); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_sret(cpu_pc, cpu_env); exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; @@ -96,9 +94,7 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) { #ifndef CONFIG_USER_ONLY decode_save_opc(ctx); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_mret(cpu_pc, cpu_env); exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index c70c495fc5..2031e9931e 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -813,9 +813,7 @@ static bool do_csrr(DisasContext *ctx, int rd, int rc) TCGv dest = dest_gpr(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_csrr(dest, cpu_env, csr); gen_set_gpr(ctx, rd, dest); return do_csr_post(ctx); @@ -825,9 +823,7 @@ static bool do_csrw(DisasContext *ctx, int rc, TCGv src) { TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_csrw(cpu_env, csr, src); return do_csr_post(ctx); } @@ -837,9 +833,7 @@ static bool do_csrrw(DisasContext *ctx, int rd, int rc, TCGv src, TCGv mask) TCGv dest = dest_gpr(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_csrrw(dest, cpu_env, csr, src, mask); gen_set_gpr(ctx, rd, dest); return do_csr_post(ctx); @@ -851,9 +845,7 @@ static bool do_csrr_i128(DisasContext *ctx, int rd, int rc) TCGv desth = dest_gprh(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_csrr_i128(destl, cpu_env, csr); tcg_gen_ld_tl(desth, cpu_env, offsetof(CPURISCVState, retxh)); gen_set_gpr128(ctx, rd, destl, desth); @@ -864,9 +856,7 @@ static bool do_csrw_i128(DisasContext *ctx, int rc, TCGv srcl, TCGv srch) { TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_csrw_i128(cpu_env, csr, srcl, srch); return do_csr_post(ctx); } @@ -878,9 +868,7 @@ static bool do_csrrw_i128(DisasContext *ctx, int rd, int rc, TCGv desth = dest_gprh(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&ctx->base); gen_helper_csrrw_i128(destl, cpu_env, csr, srcl, srch, maskl, maskh); tcg_gen_ld_tl(desth, cpu_env, offsetof(CPURISCVState, retxh)); gen_set_gpr128(ctx, rd, destl, desth); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index ed968162da..933b11c50d 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -46,8 +46,6 @@ static TCGv load_val; static TCGv pm_mask; static TCGv pm_base; -#include "exec/gen-icount.h" - /* * If an operation is being performed on less than TARGET_LONG_BITS, * it may require the inputs to be sign- or zero-extended; which will diff --git a/target/rx/translate.c b/target/rx/translate.c index 89dbec26f9..08cabbde61 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -73,8 +73,6 @@ static TCGv_i64 cpu_acc; #define cpu_sp cpu_regs[0] -#include "exec/gen-icount.h" - /* decoder helper */ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, int i, int n) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 60b17585a7..7c549cd8d0 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -38,7 +38,6 @@ #include "qemu/log.h" #include "qemu/host-utils.h" #include "exec/cpu_ldst.h" -#include "exec/gen-icount.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -6354,10 +6353,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* input/output is the special case for icount mode */ if (unlikely(insn->flags & IF_IO)) { - icount = tb_cflags(s->base.tb) & CF_USE_ICOUNT; - if (icount) { - gen_io_start(); - } + icount = translator_io_start(&s->base); } } diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 76f46d268b..49c87d7a01 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -75,8 +75,6 @@ static TCGv cpu_fregs[32]; /* internal register indexes */ static TCGv cpu_flags, cpu_delayed_pc, cpu_delayed_cond; -#include "exec/gen-icount.h" - void sh4_translate_init(void) { int i; diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ebaf376500..bad2ec90a0 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -66,8 +66,6 @@ static TCGv cpu_wim; /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; -#include "exec/gen-icount.h" - typedef struct DisasContext { DisasContextBase base; target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ @@ -3217,16 +3215,12 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_constant_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; } gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, r_const); gen_store_gpr(dc, rd, cpu_dst); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O operations in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } } break; case 0x5: /* V9 rdpc */ @@ -3269,16 +3263,12 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_constant_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; } gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, r_const); gen_store_gpr(dc, rd, cpu_dst); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O operations in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } } break; case 0x19: /* System tick compare */ @@ -3399,15 +3389,11 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_constant_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; } gen_helper_tick_get_count(cpu_tmp0, cpu_env, r_tickptr, r_const); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O operations in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } } break; case 5: // tba @@ -4212,10 +4198,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_tick_set_limit(r_tickptr, cpu_tick_cmpr); /* End TB to handle timer interrupt */ @@ -4235,10 +4218,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_tick_set_count(r_tickptr, cpu_tmp0); /* End TB to handle timer interrupt */ @@ -4258,10 +4238,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_tick_set_limit(r_tickptr, cpu_stick_cmpr); /* End TB to handle timer interrupt */ @@ -4369,10 +4346,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_tick_set_count(r_tickptr, cpu_tmp0); /* End TB to handle timer interrupt */ @@ -4384,14 +4358,10 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) break; case 6: // pstate save_state(dc); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_wrpstate(cpu_env, cpu_tmp0); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O ops in icount mode must end the TB */ + if (translator_io_start(&dc->base)) { dc->base.is_jmp = DISAS_EXIT; } + gen_helper_wrpstate(cpu_env, cpu_tmp0); dc->npc = DYNAMIC_PC; break; case 7: // tl @@ -4401,14 +4371,10 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->npc = DYNAMIC_PC; break; case 8: // pil - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_wrpil(cpu_env, cpu_tmp0); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O ops in icount mode must end the TB */ + if (translator_io_start(&dc->base)) { dc->base.is_jmp = DISAS_EXIT; } + gen_helper_wrpil(cpu_env, cpu_tmp0); break; case 9: // cwp gen_helper_wrcwp(cpu_env, cpu_tmp0); @@ -4499,10 +4465,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, hstick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_tick_set_limit(r_tickptr, cpu_hstick_cmpr); /* End TB to handle timer interrupt */ @@ -5125,9 +5088,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) goto priv_insn; dc->npc = DYNAMIC_PC; dc->pc = DYNAMIC_PC; - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_done(cpu_env); goto jmp_insn; case 1: @@ -5135,9 +5096,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) goto priv_insn; dc->npc = DYNAMIC_PC; dc->pc = DYNAMIC_PC; - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_retry(cpu_env); goto jmp_insn; default: diff --git a/target/tricore/translate.c b/target/tricore/translate.c index eee935bbaf..8e4f99478c 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -55,8 +55,6 @@ static TCGv cpu_PSW_SV; static TCGv cpu_PSW_AV; static TCGv cpu_PSW_SAV; -#include "exec/gen-icount.h" - static const char *regnames_a[] = { "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , "a6" , "a7" , "a8" , "a9" , "sp" , "a11" , diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 11bb8c079b..b7386ff0f0 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -94,8 +94,6 @@ static TCGv_i32 cpu_exclusive_val; static GHashTable *xtensa_regfile_table; -#include "exec/gen-icount.h" - static char *sr_name[256]; static char *ur_name[256]; @@ -577,9 +575,7 @@ static int gen_postprocess(DisasContext *dc, int slot) #ifndef CONFIG_USER_ONLY if (op_flags & XTENSA_OP_CHECK_INTERRUPTS) { - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_check_interrupts(cpu_env); } #endif @@ -2129,9 +2125,7 @@ static void translate_rsr_ccount(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_update_ccount(cpu_env); tcg_gen_mov_i32(arg[0].out, cpu_SR[par[0]]); #endif @@ -2447,9 +2441,7 @@ static void translate_waiti(DisasContext *dc, const OpcodeArg arg[], #ifndef CONFIG_USER_ONLY TCGv_i32 pc = tcg_constant_i32(dc->base.pc_next); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_waiti(cpu_env, pc, tcg_constant_i32(arg[0].imm)); #endif } @@ -2514,9 +2506,7 @@ static void translate_wsr_ccompare(DisasContext *dc, const OpcodeArg arg[], uint32_t id = par[0] - CCOMPARE; assert(id < dc->config->nccompare); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); tcg_gen_mov_i32(cpu_SR[par[0]], arg[0].in); gen_helper_update_ccompare(cpu_env, tcg_constant_i32(id)); #endif @@ -2526,9 +2516,7 @@ static void translate_wsr_ccount(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); gen_helper_wsr_ccount(cpu_env, arg[0].in); #endif } @@ -2715,10 +2703,7 @@ static void translate_xsr_ccount(DisasContext *dc, const OpcodeArg arg[], #ifndef CONFIG_USER_ONLY TCGv_i32 tmp = tcg_temp_new_i32(); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - + translator_io_start(&dc->base); gen_helper_update_ccount(cpu_env); tcg_gen_mov_i32(tmp, cpu_SR[par[0]]); gen_helper_wsr_ccount(cpu_env, arg[0].in); From 283a91777217226331277f60a06186bb236d3833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 2 Jun 2023 11:54:39 +0200 Subject: [PATCH 107/412] target/ppc: Inline gen_icount_io_start() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that gen_icount_io_start() is a simple wrapper to translator_io_start(), inline it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230602095439.48102-1-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/ppc/power8-pmu-regs.c.inc | 10 ++-- target/ppc/translate.c | 63 ++++++++++++-------------- target/ppc/translate/branch-impl.c.inc | 2 +- 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index d900e13cad..c82feedaff 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -103,9 +103,9 @@ static void write_MMCR0_common(DisasContext *ctx, TCGv val) /* * helper_store_mmcr0 will make clock based operations that * will cause 'bad icount read' errors if we do not execute - * gen_icount_io_start() beforehand. + * translator_io_start() beforehand. */ - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_mmcr0(cpu_env, val); /* @@ -179,7 +179,7 @@ void spr_read_PMC(DisasContext *ctx, int gprn, int sprn) { TCGv_i32 t_sprn = tcg_constant_i32(sprn); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_read_pmc(cpu_gpr[gprn], cpu_env, t_sprn); } @@ -212,7 +212,7 @@ void spr_write_PMC(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t_sprn = tcg_constant_i32(sprn); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_pmc(cpu_env, t_sprn, cpu_gpr[gprn]); } @@ -248,7 +248,7 @@ void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn) void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_mmcr1(cpu_env, cpu_gpr[gprn]); } #else diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 519f66bb05..37fd431870 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -296,15 +296,10 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_icount_io_start(DisasContext *ctx) -{ - translator_io_start(&ctx->base); -} - #if !defined(CONFIG_USER_ONLY) static void gen_ppc_maybe_interrupt(DisasContext *ctx) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_ppc_maybe_interrupt(cpu_env); } #endif @@ -541,13 +536,13 @@ void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) void spr_read_decr(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_decr(cpu_gpr[gprn], cpu_env); } void spr_write_decr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); } #endif @@ -556,13 +551,13 @@ void spr_write_decr(DisasContext *ctx, int sprn, int gprn) /* Time base */ void spr_read_tbl(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); } void spr_read_tbu(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); } @@ -579,13 +574,13 @@ void spr_read_atbu(DisasContext *ctx, int gprn, int sprn) #if !defined(CONFIG_USER_ONLY) void spr_write_tbl(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); } void spr_write_tbu(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); } @@ -602,44 +597,44 @@ void spr_write_atbu(DisasContext *ctx, int sprn, int gprn) #if defined(TARGET_PPC64) void spr_read_purr(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } void spr_write_purr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_purr(cpu_env, cpu_gpr[gprn]); } /* HDECR */ void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); } void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); } void spr_read_vtb(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_vtb(cpu_gpr[gprn], cpu_env); } void spr_write_vtb(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]); } void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]); } @@ -784,19 +779,19 @@ void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); } void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); } void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_store_spr(sprn, cpu_gpr[gprn]); gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ @@ -805,19 +800,19 @@ void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); } void spr_write_40x_tcr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_40x_tcr(cpu_env, cpu_gpr[gprn]); } void spr_write_40x_tsr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_40x_tsr(cpu_env, cpu_gpr[gprn]); } @@ -830,13 +825,13 @@ void spr_write_40x_pid(DisasContext *ctx, int sprn, int gprn) void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); } void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); } #endif @@ -2462,7 +2457,7 @@ static void gen_darn(DisasContext *ctx) if (l > 2) { tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); } else { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); if (l == 0) { gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); } else { @@ -4056,7 +4051,7 @@ static void pmu_count_insns(DisasContext *ctx) * running with icount and we do not handle it beforehand, * the helper can trigger a 'bad icount read'. */ - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); /* Avoid helper calls when only PMC5-6 are enabled. */ if (!ctx->pmc_other) { @@ -4369,7 +4364,7 @@ static void gen_rfi(DisasContext *ctx) } /* Restore CPU state */ CHK_SV(ctx); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); gen_helper_rfi(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -4384,7 +4379,7 @@ static void gen_rfid(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV(ctx); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); gen_helper_rfid(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -4399,7 +4394,7 @@ static void gen_rfscv(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV(ctx); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); gen_helper_rfscv(cpu_env); ctx->base.is_jmp = DISAS_EXIT; @@ -4724,7 +4719,7 @@ static void gen_mtmsrd(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_temp_new(); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ @@ -4764,7 +4759,7 @@ static void gen_mtmsr(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_temp_new(); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ mask &= (1ULL << MSR_RI) | (1ULL << MSR_EE); diff --git a/target/ppc/translate/branch-impl.c.inc b/target/ppc/translate/branch-impl.c.inc index 29cfa11854..f9931b9d73 100644 --- a/target/ppc/translate/branch-impl.c.inc +++ b/target/ppc/translate/branch-impl.c.inc @@ -16,7 +16,7 @@ static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg) { REQUIRE_INSNS_FLAGS2(ctx, ISA207S); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); gen_helper_rfebb(cpu_env, cpu_gpr[arg->s]); From 309e014dd10f3e98f4ca8025e7682443d4ce32f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 20:13:56 -0700 Subject: [PATCH 108/412] accel/tcg: Move translator_fake_ldb out of line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is used by exactly one host in extraordinary circumstances. This means that translator.h need not include plugin-gen.h; translator.c already includes plugin-gen.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 5 +++++ include/exec/translator.h | 8 +------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 7a130e706e..60a613c99d 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -345,3 +345,8 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) plugin_insn_append(pc, &plug, sizeof(ret)); return ret; } + +void translator_fake_ldb(uint8_t insn8, abi_ptr pc) +{ + plugin_insn_append(pc, &insn8, sizeof(insn8)); +} diff --git a/include/exec/translator.h b/include/exec/translator.h index c1a1203789..228002a623 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -22,7 +22,6 @@ #include "qemu/bswap.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" -#include "exec/plugin-gen.h" #include "exec/translate-all.h" #include "tcg/tcg.h" @@ -229,12 +228,7 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, * re-synthesised for s390x "ex"). It ensures we update other areas of * the translator with details of the executed instruction. */ - -static inline void translator_fake_ldb(uint8_t insn8, abi_ptr pc) -{ - plugin_insn_append(pc, &insn8, sizeof(insn8)); -} - +void translator_fake_ldb(uint8_t insn8, abi_ptr pc); /* * Return whether addr is on the same page as where disassembly started. From 5d05e5a1835fa3ac5c59a8d8aabef7fe99662c4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 20:38:32 -0700 Subject: [PATCH 109/412] target/arm: Tidy helpers for translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move most includes from *translate*.c to translate.h, ensuring that we get the ordering correct. Ensure cpu.h is first. Use disas/disas.h instead of exec/log.h. Drop otherwise unused includes. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 17 +++++------------ target/arm/tcg/translate-m-nocp.c | 2 -- target/arm/tcg/translate-mve.c | 3 --- target/arm/tcg/translate-neon.c | 3 --- target/arm/tcg/translate-sme.c | 6 ------ target/arm/tcg/translate-sve.c | 9 --------- target/arm/tcg/translate-vfp.c | 3 --- target/arm/tcg/translate.c | 17 +++++------------ target/arm/tcg/translate.h | 3 +++ 9 files changed, 13 insertions(+), 50 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8d45dbf8fc..d9800337cf 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -18,20 +18,13 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "qemu/log.h" -#include "arm_ldst.h" #include "translate.h" -#include "internals.h" -#include "qemu/host-utils.h" -#include "semihosting/semihost.h" -#include "exec/log.h" -#include "cpregs.h" #include "translate-a64.h" -#include "qemu/atomic128.h" +#include "qemu/log.h" +#include "disas/disas.h" +#include "arm_ldst.h" +#include "semihosting/semihost.h" +#include "cpregs.h" static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; diff --git a/target/arm/tcg/translate-m-nocp.c b/target/arm/tcg/translate-m-nocp.c index 9a89aab785..33f6478bb9 100644 --- a/target/arm/tcg/translate-m-nocp.c +++ b/target/arm/tcg/translate-m-nocp.c @@ -18,8 +18,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" #include "translate.h" #include "translate-a32.h" diff --git a/target/arm/tcg/translate-mve.c b/target/arm/tcg/translate-mve.c index 2ad3c40975..bbc7b3f4ce 100644 --- a/target/arm/tcg/translate-mve.c +++ b/target/arm/tcg/translate-mve.c @@ -18,9 +18,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" #include "translate.h" #include "translate-a32.h" diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 6fac577abd..03913de047 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -21,9 +21,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" #include "translate.h" #include "translate-a32.h" diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index b0812d9dd6..d0054e3f77 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -18,14 +18,8 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "tcg/tcg-gvec-desc.h" #include "translate.h" #include "translate-a64.h" -#include "fpu/softfloat.h" - /* * Include the generated decoder. diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 106baf311f..d9d5810dde 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -18,16 +18,7 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "tcg/tcg-gvec-desc.h" -#include "qemu/log.h" -#include "arm_ldst.h" #include "translate.h" -#include "internals.h" -#include "exec/log.h" #include "translate-a64.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 95ac8d9db3..359b1e3e96 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -21,9 +21,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" #include "translate.h" #include "translate-a32.h" diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 7caf6d802d..a68d3c7f6d 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -20,20 +20,13 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "internals.h" -#include "disas/disas.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "qemu/log.h" -#include "qemu/bitops.h" -#include "arm_ldst.h" -#include "semihosting/semihost.h" -#include "exec/log.h" -#include "cpregs.h" #include "translate.h" #include "translate-a32.h" +#include "qemu/log.h" +#include "disas/disas.h" +#include "arm_ldst.h" +#include "semihosting/semihost.h" +#include "cpregs.h" #include "exec/helper-proto.h" #define HELPER_H "helper.h" diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 868a3abd0d..5b53b6215d 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -1,6 +1,9 @@ #ifndef TARGET_ARM_TRANSLATE_H #define TARGET_ARM_TRANSLATE_H +#include "cpu.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "exec/translator.h" #include "exec/helper-gen.h" #include "internals.h" From 8cab4157e93105864c579ecac96b520bc13052f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 20:44:44 -0700 Subject: [PATCH 110/412] target/mips: Tidy helpers for translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move most includes from *translate*.c to translate.h, ensuring that we get the ordering correct. Ensure cpu.h is first. Use disas/disas.h instead of exec/log.h. Drop otherwise unused includes. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/msa_translate.c | 3 --- target/mips/tcg/mxu_translate.c | 2 -- target/mips/tcg/octeon_translate.c | 4 +--- target/mips/tcg/rel6_translate.c | 2 -- target/mips/tcg/translate.c | 18 ++++++------------ target/mips/tcg/translate.h | 6 ++++-- target/mips/tcg/translate_addr_const.c | 1 - target/mips/tcg/tx79_translate.c | 4 +--- target/mips/tcg/vr54xx_translate.c | 3 --- 9 files changed, 12 insertions(+), 31 deletions(-) diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 220cd3b048..b5b66fb38a 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -11,11 +11,8 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" #include "fpu_helper.h" -#include "internal.h" static int elm_n(DisasContext *ctx, int x); static int elm_df(DisasContext *ctx, int x); diff --git a/target/mips/tcg/mxu_translate.c b/target/mips/tcg/mxu_translate.c index be038b5f07..39348b3a91 100644 --- a/target/mips/tcg/mxu_translate.c +++ b/target/mips/tcg/mxu_translate.c @@ -16,8 +16,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" /* diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index 103c304d10..e25c4cbaa0 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -7,10 +7,8 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/helper-gen.h" #include "translate.h" +#include "tcg/tcg-op-gvec.h" /* Include the auto-generated decoder. */ #include "decode-octeon.c.inc" diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index d631851258..59f237ba3b 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -9,8 +9,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" /* Include the auto-generated decoders. */ diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 312ed66989..f3da05ba3b 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -23,19 +23,13 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "internal.h" -#include "tcg/tcg-op.h" -#include "exec/translator.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" -#include "semihosting/semihost.h" - -#include "trace.h" -#include "exec/log.h" -#include "qemu/qemu-print.h" -#include "fpu_helper.h" #include "translate.h" +#include "internal.h" +#include "exec/helper-proto.h" +#include "semihosting/semihost.h" +#include "trace.h" +#include "disas/disas.h" +#include "fpu_helper.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index fa8bf55209..3b0498a47a 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -8,9 +8,11 @@ #ifndef TARGET_MIPS_TRANSLATE_H #define TARGET_MIPS_TRANSLATE_H -#include "qemu/log.h" -#include "exec/translator.h" +#include "cpu.h" #include "tcg/tcg-op.h" +#include "exec/translator.h" +#include "exec/helper-gen.h" +#include "qemu/log.h" #define MIPS_DEBUG_DISAS 0 diff --git a/target/mips/tcg/translate_addr_const.c b/target/mips/tcg/translate_addr_const.c index a510da406c..6f4b39f715 100644 --- a/target/mips/tcg/translate_addr_const.c +++ b/target/mips/tcg/translate_addr_const.c @@ -11,7 +11,6 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" #include "translate.h" bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa) diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 3a45a1bfea..dd6fb8a7bd 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -8,10 +8,8 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/helper-gen.h" #include "translate.h" +#include "tcg/tcg-op-gvec.h" /* Include the auto-generated decoder. */ #include "decode-tx79.c.inc" diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c index 804672f84c..2c1f6cc527 100644 --- a/target/mips/tcg/vr54xx_translate.c +++ b/target/mips/tcg/vr54xx_translate.c @@ -10,10 +10,7 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" -#include "internal.h" /* Include the auto-generated decoder. */ #include "decode-vr54xx.c.inc" From d654e92817a6cece4dc96f9c736b825a3da4ee3f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 21:11:29 -0700 Subject: [PATCH 111/412] target/*: Add missing includes of exec/translation-block.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been pulled in via exec/exec-all.h, via exec/translator.h, but the include of exec-all.h will be removed. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hexagon/translate.c | 1 + target/loongarch/translate.c | 3 +-- target/mips/tcg/translate.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 770de58647..708339198e 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -22,6 +22,7 @@ #include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" #include "exec/helper-proto.h" +#include "exec/translation-block.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "internal.h" diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c index 1cf27a4611..3146a2d4ac 100644 --- a/target/loongarch/translate.c +++ b/target/loongarch/translate.c @@ -9,11 +9,10 @@ #include "cpu.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" - +#include "exec/translation-block.h" #include "exec/translator.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" - #include "exec/log.h" #include "qemu/qemu-print.h" #include "fpu/softfloat.h" diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index f3da05ba3b..74af91e4f5 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -26,6 +26,7 @@ #include "translate.h" #include "internal.h" #include "exec/helper-proto.h" +#include "exec/translation-block.h" #include "semihosting/semihost.h" #include "trace.h" #include "disas/disas.h" From 1f17f91d437ff3437cf51d3cdd8f29e59530e38d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 21:12:50 -0700 Subject: [PATCH 112/412] target/arm: Add missing include of exec/exec-all.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This had been pulled in via exec/translator.h, but the include of exec-all.h will be removed. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/translate.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 5b53b6215d..4d88197715 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -4,6 +4,7 @@ #include "cpu.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" +#include "exec/exec-all.h" #include "exec/translator.h" #include "exec/helper-gen.h" #include "internals.h" From 653c46daf20add70bf45db8b31edae8fbd584d9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 21:16:39 -0700 Subject: [PATCH 113/412] accel/tcg: Tidy includes for translator.[ch] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the header to only bswap.h and cpu_ldst.h. Move exec/translate-all.h to translator.c. Reduce tcg.h and tcg-op.h to tcg-op-common.h. Remove otherwise unused headers. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 8 +++----- include/exec/translator.h | 6 +----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 60a613c99d..fda4e7f637 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -8,15 +8,13 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/error-report.h" -#include "tcg/tcg.h" -#include "tcg/tcg-op.h" #include "exec/exec-all.h" -#include "exec/log.h" #include "exec/translator.h" +#include "exec/translate-all.h" #include "exec/plugin-gen.h" -#include "exec/replay-core.h" - +#include "tcg/tcg-op-common.h" static void gen_io_start(void) { diff --git a/include/exec/translator.h b/include/exec/translator.h index 228002a623..224ae14aa7 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -18,12 +18,8 @@ * member in your target-specific DisasContext. */ - #include "qemu/bswap.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/translate-all.h" -#include "tcg/tcg.h" +#include "exec/cpu_ldst.h" /* for abi_ptr */ /** * gen_intermediate_code From 649795665f9cb9a8862848f49100c3015f067ccf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Apr 2023 08:13:50 -0700 Subject: [PATCH 114/412] tcg: Fix PAGE/PROT confusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bug was hidden because they happen to have the same values. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/region.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tcg/region.c b/tcg/region.c index bef4c4756f..f8410ba5db 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -505,6 +505,14 @@ static int alloc_code_gen_buffer(size_t tb_size, int splitwx, Error **errp) return PROT_READ | PROT_WRITE; } #elif defined(_WIN32) +/* + * Local source-level compatibility with Unix. + * Used by tcg_region_init below. + */ +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) { void *buf; @@ -525,7 +533,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) region.start_aligned = buf; region.total_size = size; - return PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return PROT_READ | PROT_WRITE | PROT_EXEC; } #else static int alloc_code_gen_buffer_anon(size_t size, int prot, @@ -794,10 +802,10 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) * buffer -- let that one use hugepages throughout. * Work with the page protections set up with the initial mapping. */ - need_prot = PAGE_READ | PAGE_WRITE; + need_prot = PROT_READ | PROT_WRITE; #ifndef CONFIG_TCG_INTERPRETER if (tcg_splitwx_diff == 0) { - need_prot |= PAGE_EXEC; + need_prot |= PROT_EXEC; } #endif for (size_t i = 0, n = region.n; i < n; i++) { @@ -807,9 +815,9 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) if (have_prot != need_prot) { int rc; - if (need_prot == (PAGE_READ | PAGE_WRITE | PAGE_EXEC)) { + if (need_prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) { rc = qemu_mprotect_rwx(start, end - start); - } else if (need_prot == (PAGE_READ | PAGE_WRITE)) { + } else if (need_prot == (PROT_READ | PROT_WRITE)) { rc = qemu_mprotect_rw(start, end - start); } else { g_assert_not_reached(); From b78477fe1b22e9171c13a88e86a1e367779beab5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Apr 2023 08:10:46 -0700 Subject: [PATCH 115/412] tcg: Move env defines out of NEED_CPU_H in helper-head.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the change to CPUArchState, we have a common typedef that can always be used. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/helper-head.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index a355ef8ebe..28ceab0a46 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -22,6 +22,7 @@ #define dh_alias_f64 i64 #define dh_alias_ptr ptr #define dh_alias_cptr ptr +#define dh_alias_env ptr #define dh_alias_void void #define dh_alias_noreturn noreturn #define dh_alias(t) glue(dh_alias_, t) @@ -37,6 +38,7 @@ #define dh_ctype_f64 float64 #define dh_ctype_ptr void * #define dh_ctype_cptr const void * +#define dh_ctype_env CPUArchState * #define dh_ctype_void void #define dh_ctype_noreturn G_NORETURN void #define dh_ctype(t) dh_ctype_##t @@ -52,9 +54,6 @@ # endif # endif # define dh_ctype_tl target_ulong -# define dh_alias_env ptr -# define dh_ctype_env CPUArchState * -# define dh_typecode_env dh_typecode_ptr #endif /* We can't use glue() here because it falls foul of C preprocessor @@ -96,6 +95,7 @@ #define dh_typecode_f32 dh_typecode_i32 #define dh_typecode_f64 dh_typecode_i64 #define dh_typecode_cptr dh_typecode_ptr +#define dh_typecode_env dh_typecode_ptr #define dh_typecode(t) dh_typecode_##t #define dh_callflag_i32 0 From cac9b0fd08b38d5e5b3982c3ff6cd8e7fbc22e23 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 1 Apr 2023 21:22:06 -0700 Subject: [PATCH 116/412] tcg: Remove target-specific headers from tcg.[ch] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This finally paves the way for tcg/ to be built once per mode. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 1 + include/tcg/tcg.h | 1 - tcg/region.c | 2 +- tcg/tcg-op.c | 2 +- tcg/tcg.c | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 3e528f191d..5c13615112 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -43,6 +43,7 @@ * CPU's index into a TCG temp, since the first callback did it already. */ #include "qemu/osdep.h" +#include "cpu.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op.h" diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 635fa53fdb..a498f31967 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -25,7 +25,6 @@ #ifndef TCG_H #define TCG_H -#include "cpu.h" #include "exec/memop.h" #include "exec/memopidx.h" #include "qemu/bitops.h" diff --git a/tcg/region.c b/tcg/region.c index f8410ba5db..2b28ed3556 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -30,8 +30,8 @@ #include "qemu/cacheinfo.h" #include "qemu/qtree.h" #include "qapi/error.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" +#include "exec/translation-block.h" #include "tcg-internal.h" diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 8c1ad49c4e..c07de5d9f8 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -23,10 +23,10 @@ */ #include "qemu/osdep.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op-common.h" +#include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" diff --git a/tcg/tcg.c b/tcg/tcg.c index 41186f540f..3fcd0d9f32 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -34,7 +34,7 @@ #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" #include "qemu/timer.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" #include "exec/tlb-common.h" #include "tcg/tcg-op-common.h" From bc54ef8c6a5331471f2a7bdd0e81982c2fafc9e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Apr 2023 08:27:22 -0700 Subject: [PATCH 117/412] plugins: Move plugin_insn_append to translator.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is only used in translator.c, and uses a target-specific typedef: abi_ptr. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 21 +++++++++++++++++++++ include/exec/plugin-gen.h | 22 ---------------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index fda4e7f637..918a455e73 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -285,6 +285,27 @@ static void *translator_access(CPUArchState *env, DisasContextBase *db, return host + (pc - base); } +static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) +{ +#ifdef CONFIG_PLUGIN + struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; + abi_ptr off; + + if (insn == NULL) { + return; + } + off = pc - insn->vaddr; + if (off < insn->data->len) { + g_byte_array_set_size(insn->data, off); + } else if (off > insn->data->len) { + /* we have an unexpected gap */ + g_assert_not_reached(); + } + + insn->data = g_byte_array_append(insn->data, from, size); +#endif +} + uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) { uint8_t ret; diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index 3af0168e65..e9a976f815 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -29,25 +29,6 @@ void plugin_gen_insn_end(void); void plugin_gen_disable_mem_helpers(void); void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info); -static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size) -{ - struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; - abi_ptr off; - - if (insn == NULL) { - return; - } - off = pc - insn->vaddr; - if (off < insn->data->len) { - g_byte_array_set_size(insn->data, off); - } else if (off > insn->data->len) { - /* we have an unexpected gap */ - g_assert_not_reached(); - } - - insn->data = g_byte_array_append(insn->data, from, size); -} - #else /* !CONFIG_PLUGIN */ static inline bool @@ -72,9 +53,6 @@ static inline void plugin_gen_disable_mem_helpers(void) static inline void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) { } -static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size) -{ } - #endif /* CONFIG_PLUGIN */ #endif /* QEMU_PLUGIN_GEN_H */ From 6fcc02292c74a271e8f377f25b6818ea5c1f36ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Apr 2023 08:31:20 -0700 Subject: [PATCH 118/412] plugins: Drop unused headers from exec/plugin-gen.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two headers are not required for the rest of the contents of plugin-gen.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/plugin-gen.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index e9a976f815..52828781bc 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -12,8 +12,6 @@ #ifndef QEMU_PLUGIN_GEN_H #define QEMU_PLUGIN_GEN_H -#include "exec/cpu_ldst.h" -#include "qemu/plugin.h" #include "tcg/tcg.h" struct DisasContextBase; From d31b84041d4353ef310ffde23c87b78c2aa32ead Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 30 Apr 2023 08:54:23 +0100 Subject: [PATCH 119/412] exec/poison: Do not poison CONFIG_SOFTMMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_USER_ONLY is ok generically, so is CONFIG_SOFTMMU, because they are exactly opposite. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/poison.h | 1 - scripts/make-config-poison.sh | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/exec/poison.h b/include/exec/poison.h index 256736e11a..e94ee8dfef 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -85,7 +85,6 @@ #pragma GCC poison CONFIG_HVF #pragma GCC poison CONFIG_LINUX_USER #pragma GCC poison CONFIG_KVM -#pragma GCC poison CONFIG_SOFTMMU #pragma GCC poison CONFIG_WHPX #pragma GCC poison CONFIG_XEN diff --git a/scripts/make-config-poison.sh b/scripts/make-config-poison.sh index 1892854261..2b36907e23 100755 --- a/scripts/make-config-poison.sh +++ b/scripts/make-config-poison.sh @@ -4,11 +4,12 @@ if test $# = 0; then exit 0 fi -# Create list of config switches that should be poisoned in common code... -# but filter out CONFIG_TCG and CONFIG_USER_ONLY which are special. +# Create list of config switches that should be poisoned in common code, +# but filter out several which are handled manually. exec sed -n \ -e' /CONFIG_TCG/d' \ -e '/CONFIG_USER_ONLY/d' \ + -e '/CONFIG_SOFTMMU/d' \ -e '/^#define / {' \ -e 's///' \ -e 's/ .*//' \ From 514fea9eaf2136ba16e3d8ec38d156f89698ebd7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 22 May 2023 23:22:36 -0700 Subject: [PATCH 120/412] tcg: Build once for system and once for user-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create two static libraries for use by each execution mode. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/meson.build | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/tcg/meson.build b/tcg/meson.build index bdc185a485..565c60bc96 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -1,3 +1,7 @@ +if not get_option('tcg').allowed() + subdir_done() +endif + tcg_ss = ss.source_set() tcg_ss.add(files( @@ -14,8 +18,28 @@ tcg_ss.add(files( if get_option('tcg_interpreter') libffi = dependency('libffi', version: '>=3.0', required: true, method: 'pkg-config') - specific_ss.add(libffi) - specific_ss.add(files('tci.c')) + tcg_ss.add(libffi) + tcg_ss.add(files('tci.c')) endif -specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) +tcg_ss = tcg_ss.apply(config_host, strict: false) + +libtcg_user = static_library('tcg_user', + tcg_ss.sources() + genh, + name_suffix: 'fa', + c_args: '-DCONFIG_USER_ONLY', + build_by_default: have_user) + +tcg_user = declare_dependency(link_with: libtcg_user, + dependencies: tcg_ss.dependencies()) +user_ss.add(tcg_user) + +libtcg_softmmu = static_library('tcg_softmmu', + tcg_ss.sources() + genh, + name_suffix: 'fa', + c_args: '-DCONFIG_SOFTMMU', + build_by_default: have_system) + +tcg_softmmu = declare_dependency(link_with: libtcg_softmmu, + dependencies: tcg_ss.dependencies()) +softmmu_ss.add(tcg_softmmu) From e7cd7a3916ac50378479b773057100683414eec4 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 5 Jun 2023 13:41:34 +0200 Subject: [PATCH 121/412] accel/tcg: Unmap perf_marker Coverity complains that perf_marker is never unmapped. Fix by unmapping it in perf_exit(). Fixes: Coverity CID 1507929 Fixes: 5584e2dbe8c9 ("tcg: add perfmap and jitdump") Signed-off-by: Ilya Leoshkevich Message-Id: <20230605114134.1169974-1-iii@linux.ibm.com> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/perf.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/accel/tcg/perf.c b/accel/tcg/perf.c index f5a1eda39f..cd1aa99a7e 100644 --- a/accel/tcg/perf.c +++ b/accel/tcg/perf.c @@ -111,6 +111,8 @@ static void write_perfmap_entry(const void *start, size_t insn, } static FILE *jitdump; +static size_t perf_marker_size; +static void *perf_marker = MAP_FAILED; #define JITHEADER_MAGIC 0x4A695444 #define JITHEADER_VERSION 1 @@ -190,7 +192,6 @@ void perf_enable_jitdump(void) { struct jitheader header; char jitdump_file[32]; - void *perf_marker; if (!use_rt_clock) { warn_report("CLOCK_MONOTONIC is not available, proceeding without jitdump"); @@ -210,7 +211,8 @@ void perf_enable_jitdump(void) * PERF_RECORD_MMAP or PERF_RECORD_MMAP2 event is of the form jit-%d.dump * and will process it as a jitdump file. */ - perf_marker = mmap(NULL, qemu_real_host_page_size(), PROT_READ | PROT_EXEC, + perf_marker_size = qemu_real_host_page_size(); + perf_marker = mmap(NULL, perf_marker_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(jitdump), 0); if (perf_marker == MAP_FAILED) { warn_report("Could not map %s: %s, proceeding without jitdump", @@ -372,6 +374,11 @@ void perf_exit(void) perfmap = NULL; } + if (perf_marker != MAP_FAILED) { + munmap(perf_marker, perf_marker_size); + perf_marker = MAP_FAILED; + } + if (jitdump) { fclose(jitdump); jitdump = NULL; From a7f6911c127b1dd1b8764e03b0ebcf0a227a15e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 5 Jun 2023 19:56:44 +0200 Subject: [PATCH 122/412] tcg/tcg-op-vec: Remove left over _link_error() definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit d56fea79f9 ("tcg: Move TCG_{LOW,HIGH} to tcg-internal.h") we replaced the "_link_error" definitions with modern QEMU_ERROR() attribute markup. We covered tcg-op.c but forgot to completely clean tcg-op-vec.c. Do it now. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230605175647.88395-3-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- tcg/tcg-op-vec.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 35d67eeda0..64bc8a2156 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -24,17 +24,6 @@ #include "tcg/tcg-mo.h" #include "tcg-internal.h" - -/* Reduce the number of ifdefs below. This assumes that all uses of - TCGV_HIGH and TCGV_LOW are properly protected by a conditional that - the compiler can eliminate. */ -#if TCG_TARGET_REG_BITS == 64 -extern TCGv_i32 TCGV_LOW_link_error(TCGv_i64); -extern TCGv_i32 TCGV_HIGH_link_error(TCGv_i64); -#define TCGV_LOW TCGV_LOW_link_error -#define TCGV_HIGH TCGV_HIGH_link_error -#endif - /* * Vector optional opcode tracking. * Except for the basic logical operations (and, or, xor), and From 09a49afeae2542993d4cdc5d7af22abdfce7a3ba Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Tue, 30 May 2023 12:31:23 +0200 Subject: [PATCH 123/412] meson: Avoid implicit declaration of absent functions While detecting a presence of a function via 'cc.links()' gives desired result (i.e. detects whether function is present), it also produces a warning on systems where the function is not present (into meson-log.txt), e.g.: qemu.git/build/meson-private/tmph74x3p38/testfile.c:2:34: \ warning: implicit declaration of function 'malloc_trim' [-Wimplicit-function-declaration] And some distributions (e.g. Gentoo) parse the meson log and consider these erroneous because it can lead to feature misdetection (see [1]). We can check whether given function exists via 'cc.has_function()' or whether STATX_* macros exist via 'cc.has_header_symbol()'. 1: https://wiki.gentoo.org/wiki/Modern_C_porting Resolves: https://bugs.gentoo.org/898810 Signed-off-by: Michal Privoznik Message-Id: <8e02776d18595a1c575c90a189ff65f1785f76ca.1685442612.git.mprivozn@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/meson.build b/meson.build index a61d3e9b06..969a84f2a4 100644 --- a/meson.build +++ b/meson.build @@ -1797,8 +1797,7 @@ malloc = [] if get_option('malloc') == 'system' has_malloc_trim = \ get_option('malloc_trim').allowed() and \ - cc.links('''#include - int main(void) { malloc_trim(0); return 0; }''') + cc.has_function('malloc_trim', prefix: '#include ') else has_malloc_trim = false malloc = cc.find_library(get_option('malloc'), required: true) @@ -1811,34 +1810,19 @@ if not has_malloc_trim and get_option('malloc_trim').enabled() endif endif -# Check whether the glibc provides statx() - gnu_source_prefix = ''' #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif ''' -statx_test = gnu_source_prefix + ''' - #include - int main(void) { - struct statx statxbuf; - statx(0, "", 0, STATX_BASIC_STATS, &statxbuf); - return 0; - }''' -has_statx = cc.links(statx_test) +# Check whether the glibc provides STATX_BASIC_STATS + +has_statx = cc.has_header_symbol('sys/stat.h', 'STATX_BASIC_STATS', prefix: gnu_source_prefix) # Check whether statx() provides mount ID information -statx_mnt_id_test = gnu_source_prefix + ''' - #include - int main(void) { - struct statx statxbuf; - statx(0, "", 0, STATX_BASIC_STATS | STATX_MNT_ID, &statxbuf); - return statxbuf.stx_mnt_id; - }''' - -has_statx_mnt_id = cc.links(statx_mnt_id_test) +has_statx_mnt_id = cc.has_header_symbol('sys/stat.h', 'STATX_MNT_ID', prefix: gnu_source_prefix) have_vhost_user_blk_server = get_option('vhost_user_blk_server') \ .require(targetos == 'linux', From 06831001ac8949b0801e0d20c347d97339769a20 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 Mar 2023 14:37:51 +0100 Subject: [PATCH 124/412] atomics: eliminate mb_read/mb_set qatomic_mb_read and qatomic_mb_set were the very first atomic primitives introduced for QEMU; their semantics are unclear and they provide a false sense of safety. The last use of qatomic_mb_read() has been removed, so delete it. qatomic_mb_set() instead can survive as an optimized qatomic_set()+smp_mb(), similar to Linux's smp_store_mb(), but rename it to qatomic_set_mb() to match the order of the two operations. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 4 ++-- docs/devel/atomics.rst | 27 ++++----------------------- include/qemu/atomic.h | 17 +++++------------ monitor/qmp.c | 2 +- softmmu/cpus.c | 2 +- softmmu/physmem.c | 2 +- target/arm/hvf/hvf.c | 2 +- tests/unit/test-aio-multithread.c | 2 +- util/qemu-coroutine-lock.c | 4 ++-- 11 files changed, 20 insertions(+), 46 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 1cf4f1fa22..42086525d7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -774,7 +774,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * Ensure zeroing happens before reading cpu->exit_request or * cpu->interrupt_request (see also smp_wmb in cpu_exit()) */ - qatomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0); + qatomic_set_mb(&cpu_neg(cpu)->icount_decr.u16.high, 0); if (unlikely(qatomic_read(&cpu->interrupt_request))) { int interrupt_request; diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 5d72c9b1bd..b320ff0037 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -119,7 +119,7 @@ static void *mttcg_cpu_thread_fn(void *arg) } } - qatomic_mb_set(&cpu->exit_request, 0); + qatomic_set_mb(&cpu->exit_request, 0); qemu_wait_io_event(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 70b9b89073..23e4d0f452 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -244,7 +244,7 @@ static void *rr_cpu_thread_fn(void *arg) while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { /* Store rr_current_cpu before evaluating cpu_can_run(). */ - qatomic_mb_set(&rr_current_cpu, cpu); + qatomic_set_mb(&rr_current_cpu, cpu); current_cpu = cpu; @@ -287,7 +287,7 @@ static void *rr_cpu_thread_fn(void *arg) qatomic_set(&rr_current_cpu, NULL); if (cpu && cpu->exit_request) { - qatomic_mb_set(&cpu->exit_request, 0); + qatomic_set_mb(&cpu->exit_request, 0); } if (icount_enabled() && all_cpu_threads_idle()) { diff --git a/docs/devel/atomics.rst b/docs/devel/atomics.rst index 248076375b..ff9b5ee30c 100644 --- a/docs/devel/atomics.rst +++ b/docs/devel/atomics.rst @@ -102,28 +102,10 @@ Similar operations return the new value of ``*ptr``:: typeof(*ptr) qatomic_or_fetch(ptr, val) typeof(*ptr) qatomic_xor_fetch(ptr, val) -``qemu/atomic.h`` also provides loads and stores that cannot be reordered -with each other:: +``qemu/atomic.h`` also provides an optimized shortcut for +``qatomic_set`` followed by ``smp_mb``:: - typeof(*ptr) qatomic_mb_read(ptr) - void qatomic_mb_set(ptr, val) - -However these do not provide sequential consistency and, in particular, -they do not participate in the total ordering enforced by -sequentially-consistent operations. For this reason they are deprecated. -They should instead be replaced with any of the following (ordered from -easiest to hardest): - -- accesses inside a mutex or spinlock - -- lightweight synchronization primitives such as ``QemuEvent`` - -- RCU operations (``qatomic_rcu_read``, ``qatomic_rcu_set``) when publishing - or accessing a new version of a data structure - -- other atomic accesses: ``qatomic_read`` and ``qatomic_load_acquire`` for - loads, ``qatomic_set`` and ``qatomic_store_release`` for stores, ``smp_mb`` - to forbid reordering subsequent loads before a store. + void qatomic_set_mb(ptr, val) Weak atomic access and manual memory barriers @@ -523,8 +505,7 @@ and memory barriers, and the equivalents in QEMU: | :: | | | | a = qatomic_read(&x); | - | qatomic_set(&x, a + 2); | - | smp_mb(); | + | qatomic_set_mb(&x, a + 2); | | b = qatomic_read(&y); | +--------------------------------+ diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index f85834ee8b..d95612f7a0 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -259,24 +259,17 @@ # define smp_mb__after_rmw() smp_mb() #endif -/* qatomic_mb_read/set semantics map Java volatile variables. They are - * less expensive on some platforms (notably POWER) than fully - * sequentially consistent operations. - * - * As long as they are used as paired operations they are safe to - * use. See docs/devel/atomics.rst for more discussion. +/* + * On some architectures, qatomic_set_mb is more efficient than a store + * plus a fence. */ -#define qatomic_mb_read(ptr) \ - qatomic_load_acquire(ptr) - #if !defined(QEMU_SANITIZE_THREAD) && \ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) -/* This is more efficient than a store plus a fence. */ -# define qatomic_mb_set(ptr, i) \ +# define qatomic_set_mb(ptr, i) \ ({ (void)qatomic_xchg(ptr, i); smp_mb__after_rmw(); }) #else -# define qatomic_mb_set(ptr, i) \ +# define qatomic_set_mb(ptr, i) \ ({ qatomic_store_release(ptr, i); smp_mb(); }) #endif diff --git a/monitor/qmp.c b/monitor/qmp.c index c8e0156974..6eee450fe4 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -246,7 +246,7 @@ static QMPRequest *monitor_qmp_dispatcher_pop_any(void) * * Clear qmp_dispatcher_co_busy before reading request. */ - qatomic_mb_set(&qmp_dispatcher_co_busy, false); + qatomic_set_mb(&qmp_dispatcher_co_busy, false); WITH_QEMU_LOCK_GUARD(&monitor_lock) { QMPRequest *req_obj; diff --git a/softmmu/cpus.c b/softmmu/cpus.c index 9cbc8172b5..fed20ffb5d 100644 --- a/softmmu/cpus.c +++ b/softmmu/cpus.c @@ -405,7 +405,7 @@ static void qemu_cpu_stop(CPUState *cpu, bool exit) void qemu_wait_io_event_common(CPUState *cpu) { - qatomic_mb_set(&cpu->thread_kicked, false); + qatomic_set_mb(&cpu->thread_kicked, false); if (cpu->stop) { qemu_cpu_stop(cpu, false); } diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 9d7e172260..588d0d166b 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -3132,7 +3132,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, bounce.buffer = NULL; memory_region_unref(bounce.mr); /* Clear in_use before reading map_client_list. */ - qatomic_mb_set(&bounce.in_use, false); + qatomic_set_mb(&bounce.in_use, false); cpu_notify_map_clients(); } diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index ad65603445..5900dc788f 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1229,7 +1229,7 @@ static void hvf_wait_for_ipi(CPUState *cpu, struct timespec *ts) * Use pselect to sleep so that other threads can IPI us while we're * sleeping. */ - qatomic_mb_set(&cpu->thread_kicked, false); + qatomic_set_mb(&cpu->thread_kicked, false); qemu_mutex_unlock_iothread(); pselect(0, 0, 0, 0, ts, &cpu->hvf->unblock_ipi_mask); qemu_mutex_lock_iothread(); diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index 80c5d4e2e6..08d4570ccb 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -154,7 +154,7 @@ static coroutine_fn void test_multi_co_schedule_entry(void *opaque) n = g_test_rand_int_range(0, NUM_CONTEXTS); schedule_next(n); - qatomic_mb_set(&to_schedule[id], qemu_coroutine_self()); + qatomic_set_mb(&to_schedule[id], qemu_coroutine_self()); /* finish_cb can run here. */ qemu_coroutine_yield(); g_assert(to_schedule[id] == NULL); diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index 84a50a9e91..2534435388 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -202,7 +202,7 @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, push_waiter(mutex, &w); /* - * Add waiter before reading mutex->handoff. Pairs with qatomic_mb_set + * Add waiter before reading mutex->handoff. Pairs with qatomic_set_mb * in qemu_co_mutex_unlock. */ smp_mb__after_rmw(); @@ -310,7 +310,7 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) our_handoff = mutex->sequence; /* Set handoff before checking for waiters. */ - qatomic_mb_set(&mutex->handoff, our_handoff); + qatomic_set_mb(&mutex->handoff, our_handoff); if (!has_waiters(mutex)) { /* The concurrent lock has not added itself yet, so it * will be able to pick our handoff. From 0dec4e6f7a90a3b6c992ef212eefa058c4f7ce9a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 3 Jun 2023 23:09:32 +0200 Subject: [PATCH 125/412] scripts: remove dead file scripts/test-driver.py was used when "make check" was already using meson introspection data, but it did not execute "meson test". It is dead since commit 3d2f73ef75e ("build: use "meson test" as the test harness", 2021-12-23). Signed-off-by: Paolo Bonzini --- scripts/test-driver.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 scripts/test-driver.py diff --git a/scripts/test-driver.py b/scripts/test-driver.py deleted file mode 100644 index eef74b29a8..0000000000 --- a/scripts/test-driver.py +++ /dev/null @@ -1,35 +0,0 @@ -#! /usr/bin/env python3 - -# Wrapper for tests that hides the output if they succeed. -# Used by "make check" -# -# Copyright (C) 2020 Red Hat, Inc. -# -# Author: Paolo Bonzini - -import subprocess -import sys -import os -import argparse - -parser = argparse.ArgumentParser(description='Test driver for QEMU') -parser.add_argument('-C', metavar='DIR', dest='dir', default='.', - help='change to DIR before doing anything else') -parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', - help='be more verbose') -parser.add_argument('test_args', nargs=argparse.REMAINDER) - -args = parser.parse_args() -os.chdir(args.dir) - -test_args = args.test_args -if test_args[0] == '--': - test_args = test_args[1:] - -if args.verbose: - result = subprocess.run(test_args, stdout=None, stderr=None) -else: - result = subprocess.run(test_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if result.returncode: - sys.stdout.buffer.write(result.stdout) -sys.exit(result.returncode) From 9e48afa3ceb695685a0ae756384fa83594bc5f16 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 2 Jun 2023 19:18:30 +0200 Subject: [PATCH 126/412] meson.build: Group the UI entries in a separate summary section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's make it easier for the users to spot UI-related entries in the summary of the meson output. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230602171832.533739-2-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/meson.build b/meson.build index 969a84f2a4..cfacd662ed 100644 --- a/meson.build +++ b/meson.build @@ -4227,32 +4227,43 @@ summary_info += {'rng-none': get_option('rng_none')} summary_info += {'Linux keyring': have_keyring} summary(summary_info, bool_yn: true, section: 'Crypto') -# Libraries +# UI summary_info = {} if targetos == 'darwin' summary_info += {'Cocoa support': cocoa} - summary_info += {'vmnet.framework support': vmnet} endif summary_info += {'SDL support': sdl} summary_info += {'SDL image support': sdl_image} summary_info += {'GTK support': gtk} summary_info += {'pixman': pixman} summary_info += {'VTE support': vte} -summary_info += {'slirp support': slirp} -summary_info += {'libtasn1': tasn1} -summary_info += {'PAM': pam} -summary_info += {'iconv support': iconv} -summary_info += {'curses support': curses} -summary_info += {'virgl support': virgl} -summary_info += {'blkio support': blkio} -summary_info += {'curl support': curl} -summary_info += {'Multipath support': mpathpersist} summary_info += {'PNG support': png} summary_info += {'VNC support': vnc} if vnc.found() summary_info += {'VNC SASL support': sasl} summary_info += {'VNC JPEG support': jpeg} endif +summary_info += {'spice protocol support': spice_protocol} +if spice_protocol.found() + summary_info += {' spice server support': spice} +endif +summary_info += {'curses support': curses} +summary_info += {'brlapi support': brlapi} +summary(summary_info, bool_yn: true, section: 'User interface') + +# Libraries +summary_info = {} +if targetos == 'darwin' + summary_info += {'vmnet.framework support': vmnet} +endif +summary_info += {'slirp support': slirp} +summary_info += {'libtasn1': tasn1} +summary_info += {'PAM': pam} +summary_info += {'iconv support': iconv} +summary_info += {'virgl support': virgl} +summary_info += {'blkio support': blkio} +summary_info += {'curl support': curl} +summary_info += {'Multipath support': mpathpersist} if targetos not in ['darwin', 'haiku', 'windows'] summary_info += {'OSS support': oss} summary_info += {'sndio support': sndio} @@ -4267,7 +4278,6 @@ if targetos == 'linux' endif summary_info += {'Pipewire support': pipewire} summary_info += {'JACK support': jack} -summary_info += {'brlapi support': brlapi} summary_info += {'vde support': vde} summary_info += {'netmap support': have_netmap} summary_info += {'l2tpv3 support': have_l2tpv3} @@ -4279,10 +4289,6 @@ summary_info += {'PVRDMA support': have_pvrdma} summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} -summary_info += {'spice protocol support': spice_protocol} -if spice_protocol.found() - summary_info += {' spice server support': spice} -endif summary_info += {'rbd support': rbd} summary_info += {'smartcard support': cacard} summary_info += {'U2F support': u2f} From c3527c5e492f2ee37bd0697de3fe8dbb9dbffa24 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 2 Jun 2023 19:18:31 +0200 Subject: [PATCH 127/412] meson.build: Group the network backend entries in a separate summary section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's make it easier for the users to spot network-related entries in the summary of the meson output. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230602171832.533739-3-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index cfacd662ed..b0adf5b461 100644 --- a/meson.build +++ b/meson.build @@ -4251,12 +4251,19 @@ summary_info += {'curses support': curses} summary_info += {'brlapi support': brlapi} summary(summary_info, bool_yn: true, section: 'User interface') -# Libraries +# Network backends summary_info = {} if targetos == 'darwin' summary_info += {'vmnet.framework support': vmnet} endif summary_info += {'slirp support': slirp} +summary_info += {'vde support': vde} +summary_info += {'netmap support': have_netmap} +summary_info += {'l2tpv3 support': have_l2tpv3} +summary(summary_info, bool_yn: true, section: 'Network backends') + +# Libraries +summary_info = {} summary_info += {'libtasn1': tasn1} summary_info += {'PAM': pam} summary_info += {'iconv support': iconv} @@ -4278,9 +4285,6 @@ if targetos == 'linux' endif summary_info += {'Pipewire support': pipewire} summary_info += {'JACK support': jack} -summary_info += {'vde support': vde} -summary_info += {'netmap support': have_netmap} -summary_info += {'l2tpv3 support': have_l2tpv3} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} From aece7238c4ebf4d95780df95f1a4743a7fa52ca9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 2 Jun 2023 19:18:32 +0200 Subject: [PATCH 128/412] meson.build: Group the audio backend entries in a separate summary section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's make it easier for the users to spot audio-related entries in the summary of the meson output. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230602171832.533739-4-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/meson.build b/meson.build index b0adf5b461..97b32e9beb 100644 --- a/meson.build +++ b/meson.build @@ -4251,6 +4251,24 @@ summary_info += {'curses support': curses} summary_info += {'brlapi support': brlapi} summary(summary_info, bool_yn: true, section: 'User interface') +# Audio backends +summary_info = {} +if targetos not in ['darwin', 'haiku', 'windows'] + summary_info += {'OSS support': oss} + summary_info += {'sndio support': sndio} +elif targetos == 'darwin' + summary_info += {'CoreAudio support': coreaudio} +elif targetos == 'windows' + summary_info += {'DirectSound support': dsound} +endif +if targetos == 'linux' + summary_info += {'ALSA support': alsa} + summary_info += {'PulseAudio support': pulse} +endif +summary_info += {'Pipewire support': pipewire} +summary_info += {'JACK support': jack} +summary(summary_info, bool_yn: true, section: 'Audio backends') + # Network backends summary_info = {} if targetos == 'darwin' @@ -4271,20 +4289,6 @@ summary_info += {'virgl support': virgl} summary_info += {'blkio support': blkio} summary_info += {'curl support': curl} summary_info += {'Multipath support': mpathpersist} -if targetos not in ['darwin', 'haiku', 'windows'] - summary_info += {'OSS support': oss} - summary_info += {'sndio support': sndio} -elif targetos == 'darwin' - summary_info += {'CoreAudio support': coreaudio} -elif targetos == 'windows' - summary_info += {'DirectSound support': dsound} -endif -if targetos == 'linux' - summary_info += {'ALSA support': alsa} - summary_info += {'PulseAudio support': pulse} -endif -summary_info += {'Pipewire support': pipewire} -summary_info += {'JACK support': jack} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} From 6da5f22b65f7147e1f0bd589cbdb072305a13bb7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 5 Jun 2023 13:45:23 +0200 Subject: [PATCH 129/412] meson.build: Use -Wno-undef only for SDL2 versions that need it There is no need to disable this useful compiler warning for all versions of the SDL. Unfortunately, various versions are buggy (beside SDL 2.0.8, the version 2.26.0 and 2.26.1 are broken, too, see https://github.com/libsdl-org/SDL/issues/6619 ), but we can use a simple compiler check to see whether we need the -Wno-undef or not. This also enables the printing of the version number with good versions of the SDL in the summary of the meson output again. Signed-off-by: Thomas Huth Message-Id: <20230605114523.282987-1-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 97b32e9beb..6bbeffe571 100644 --- a/meson.build +++ b/meson.build @@ -1273,10 +1273,16 @@ if not get_option('sdl').auto() or have_system sdl_image = not_found endif if sdl.found() - # work around 2.0.8 bug - sdl = declare_dependency(compile_args: '-Wno-undef', - dependencies: sdl, - version: sdl.version()) + # Some versions of SDL have problems with -Wundef + if not cc.compiles(''' + #include + #include + int main(int argc, char *argv[]) { return 0; } + ''', dependencies: sdl, args: '-Werror=undef') + sdl = declare_dependency(compile_args: '-Wno-undef', + dependencies: sdl, + version: sdl.version()) + endif sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), method: 'pkg-config') else From 6b40847ab40dba9eb1b2b0c530fcb7d484966d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 5 Jun 2023 19:41:45 +0200 Subject: [PATCH 130/412] scsi/qemu-pr-helper: Drop support for 'old' libmultipath API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1b0578f5c4 ("qemu-pr-helper: Fix build on CentOS 7") added code to probe for 'old' libmultipath API on CentOS 7. However since merge commit 8c345b3e6a (June 2021) we don't build/test CentOS 7 as it felt out of our list of supported distrib versions. Therefore we can safely remove the 'old' API check (mostly reverting commit 1b0578f5c4, except the code got converted to meson in commit 6ec0e15d95 "meson: move libmpathpersist test"). Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230605174146.87440-2-philmd@linaro.org> Signed-off-by: Paolo Bonzini --- meson.build | 19 ++----------------- scsi/qemu-pr-helper.c | 4 ---- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/meson.build b/meson.build index 6bbeffe571..6767a0d3fa 100644 --- a/meson.build +++ b/meson.build @@ -1092,9 +1092,8 @@ endif mpathlibs = [libudev] mpathpersist = not_found -mpathpersist_new_api = false if targetos == 'linux' and have_tools and get_option('mpath').allowed() - mpath_test_source_new = ''' + mpath_test_source = ''' #include #include unsigned mpath_mx_alloc_len = 1024; @@ -1111,16 +1110,6 @@ if targetos == 'linux' and have_tools and get_option('mpath').allowed() multipath_conf = mpath_lib_init(); return 0; }''' - mpath_test_source_old = ''' - #include - #include - unsigned mpath_mx_alloc_len = 1024; - int logsink; - int main(void) { - struct udev *udev = udev_new(); - mpath_lib_init(udev); - return 0; - }''' libmpathpersist = cc.find_library('mpathpersist', required: get_option('mpath')) if libmpathpersist.found() @@ -1139,10 +1128,7 @@ if targetos == 'linux' and have_tools and get_option('mpath').allowed() endforeach if mpathlibs.length() == 0 msg = 'Dependencies missing for libmpathpersist' - elif cc.links(mpath_test_source_new, dependencies: mpathlibs) - mpathpersist = declare_dependency(dependencies: mpathlibs) - mpathpersist_new_api = true - elif cc.links(mpath_test_source_old, dependencies: mpathlibs) + elif cc.links(mpath_test_source, dependencies: mpathlibs) mpathpersist = declare_dependency(dependencies: mpathlibs) else msg = 'Cannot detect libmpathpersist API' @@ -2094,7 +2080,6 @@ config_host_data.set('CONFIG_GCOV', get_option('b_coverage')) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) config_host_data.set('CONFIG_LZO', lzo.found()) config_host_data.set('CONFIG_MPATH', mpathpersist.found()) -config_host_data.set('CONFIG_MPATH_NEW_API', mpathpersist_new_api) config_host_data.set('CONFIG_BLKIO', blkio.found()) if blkio.found() config_host_data.set('CONFIG_BLKIO_VHOST_VDPA_FD', diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index a857e80c03..ae44a816e1 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -280,11 +280,7 @@ void put_multipath_config(struct config *conf) static void multipath_pr_init(void) { udev = udev_new(); -#ifdef CONFIG_MPATH_NEW_API multipath_conf = mpath_lib_init(); -#else - mpath_lib_init(udev); -#endif } static int is_mpath(int fd) From eaf245becd9ece615a4831c0cf4d27b165d8675f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 3 Jun 2023 23:54:57 +0200 Subject: [PATCH 131/412] Revert "tests/requirements.txt: bump up avocado-framework version to 101.0" This reverts commit ec5ffa0056389c3c10ea2de1e78366f66f4e5abc. Bumping avocado to version 101 has two issues. First, there are problems where Avocado is not logging of command lines or terminal output, and not collecting Python logs outside the avocado namespace. Second, the recent changes to Python handling mean that there is a single virtual environment for all the build, instead of a separate one for testing. Requiring a too-new version of avocado causes conflicts with any avocado plugins installed on the host: $ make check-venv make[1]: Entering directory '/home/berrange/src/virt/qemu/build' GIT ui/keycodemapdb tests/fp/berkeley-testfloat-3 tests/fp/berkeley-softfloat-3 dtc VENVPIP install -e /home/berrange/src/virt/qemu/python/ VENVPIP install -r /home/berrange/src/virt/qemu/tests/requirements.txt ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. avocado-framework-plugin-varianter-yaml-to-mux 98.0 requires avocado-framework==98.0, but you have avocado-framework 101.0 which is incompatible. avocado-framework-plugin-result-html 98.0 requires avocado-framework==98.0, but you have avocado-framework 101.0 which is incompatible. make[1]: Leaving directory '/home/berrange/src/virt/qemu/build' To avoid this issue, tests/requirements.txt should use a ">=" constraint and the version of Avocado should be limited to what distros provide in the system packages. Only Fedora has Avocado, and more specifically version 92.0 (though 98.0 is also available as a module). As a first step, this patch reverts the introduction of a too-new Avocado. Signed-off-by: Paolo Bonzini --- tests/Makefile.include | 18 +++++++----------- tests/requirements.txt | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 0184ef2237..8294a44816 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -136,18 +136,14 @@ get-vm-image-fedora-31-%: check-venv # download all vm images, according to defined targets get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) -JOBS_OPTION=$(lastword -j1 $(filter-out -j, $(filter -j%,$(MAKEFLAGS)))) - check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images - $(call quiet-command, \ - $(PYTHON) -m avocado \ - --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ - $(if $(AVOCADO_TAGS),, \ - --filter-by-tags-include-empty \ - --filter-by-tags-include-empty-key) \ - --max-parallel-tasks $(JOBS_OPTION:-j%=%) \ - $(AVOCADO_CMDLINE_TAGS) \ - $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ + $(call quiet-command, \ + $(PYTHON) -m avocado \ + --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ + $(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \ + --filter-by-tags-include-empty-key) \ + $(AVOCADO_CMDLINE_TAGS) \ + $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ "AVOCADO", "tests/avocado") check-acceptance-deprecated-warning: diff --git a/tests/requirements.txt b/tests/requirements.txt index 0e008b9aec..07e713ef5a 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -5,5 +5,5 @@ # Note that qemu.git/python/ is implicitly installed to this venv when # 'make check-venv' is run, and will persist until configure is run # again. -avocado-framework==101.0 +avocado-framework==88.1 pycdlib==1.11.0 From 0ca52a5fedeae3987ab2a84077d8ecb7d913aa37 Mon Sep 17 00:00:00 2001 From: Francesco Cagnin Date: Tue, 6 Jun 2023 10:19:29 +0100 Subject: [PATCH 132/412] arm: move KVM breakpoints helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These helpers will be also used for HVF. Aside from reformatting a couple of comments for 'checkpatch.pl' and updating meson to compile 'hyp_gdbstub.c', this is just code motion. Signed-off-by: Francesco Cagnin Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Message-id: 20230601153107.81955-2-fcagnin@quarkslab.com Signed-off-by: Peter Maydell --- target/arm/hyp_gdbstub.c | 253 +++++++++++++++++++++++++++++++++++ target/arm/internals.h | 50 +++++++ target/arm/kvm64.c | 276 --------------------------------------- target/arm/meson.build | 3 +- 4 files changed, 305 insertions(+), 277 deletions(-) create mode 100644 target/arm/hyp_gdbstub.c diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c new file mode 100644 index 0000000000..ebde2899cd --- /dev/null +++ b/target/arm/hyp_gdbstub.c @@ -0,0 +1,253 @@ +/* + * ARM implementation of KVM and HVF hooks, 64 bit specific code + * + * Copyright Mian-M. Hamayun 2013, Virtual Open Systems + * Copyright Alex Bennée 2014, Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "exec/gdbstub.h" + +/* Maximum and current break/watch point counts */ +int max_hw_bps, max_hw_wps; +GArray *hw_breakpoints, *hw_watchpoints; + +/** + * insert_hw_breakpoint() + * @addr: address of breakpoint + * + * See ARM ARM D2.9.1 for details but here we are only going to create + * simple un-linked breakpoints (i.e. we don't chain breakpoints + * together to match address and context or vmid). The hardware is + * capable of fancier matching but that will require exposing that + * fanciness to GDB's interface + * + * DBGBCR_EL1, Debug Breakpoint Control Registers + * + * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0 + * +------+------+-------+-----+----+------+-----+------+-----+---+ + * | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E | + * +------+------+-------+-----+----+------+-----+------+-----+---+ + * + * BT: Breakpoint type (0 = unlinked address match) + * LBN: Linked BP number (0 = unused) + * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12) + * BAS: Byte Address Select (RES1 for AArch64) + * E: Enable bit + * + * DBGBVR_EL1, Debug Breakpoint Value Registers + * + * 63 53 52 49 48 2 1 0 + * +------+-----------+----------+-----+ + * | RESS | VA[52:49] | VA[48:2] | 0 0 | + * +------+-----------+----------+-----+ + * + * Depending on the addressing mode bits the top bits of the register + * are a sign extension of the highest applicable VA bit. Some + * versions of GDB don't do it correctly so we ensure they are correct + * here so future PC comparisons will work properly. + */ + +int insert_hw_breakpoint(target_ulong addr) +{ + HWBreakpoint brk = { + .bcr = 0x1, /* BCR E=1, enable */ + .bvr = sextract64(addr, 0, 53) + }; + + if (cur_hw_bps >= max_hw_bps) { + return -ENOBUFS; + } + + brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */ + brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */ + + g_array_append_val(hw_breakpoints, brk); + + return 0; +} + +/** + * delete_hw_breakpoint() + * @pc: address of breakpoint + * + * Delete a breakpoint and shuffle any above down + */ + +int delete_hw_breakpoint(target_ulong pc) +{ + int i; + for (i = 0; i < hw_breakpoints->len; i++) { + HWBreakpoint *brk = get_hw_bp(i); + if (brk->bvr == pc) { + g_array_remove_index(hw_breakpoints, i); + return 0; + } + } + return -ENOENT; +} + +/** + * insert_hw_watchpoint() + * @addr: address of watch point + * @len: size of area + * @type: type of watch point + * + * See ARM ARM D2.10. As with the breakpoints we can do some advanced + * stuff if we want to. The watch points can be linked with the break + * points above to make them context aware. However for simplicity + * currently we only deal with simple read/write watch points. + * + * D7.3.11 DBGWCR_EL1, Debug Watchpoint Control Registers + * + * 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0 + * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ + * | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E | + * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ + * + * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes)) + * WT: 0 - unlinked, 1 - linked (not currently used) + * LBN: Linked BP number (not currently used) + * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11) + * BAS: Byte Address Select + * LSC: Load/Store control (01: load, 10: store, 11: both) + * E: Enable + * + * The bottom 2 bits of the value register are masked. Therefore to + * break on any sizes smaller than an unaligned word you need to set + * MASK=0, BAS=bit per byte in question. For larger regions (^2) you + * need to ensure you mask the address as required and set BAS=0xff + */ + +int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) +{ + HWWatchpoint wp = { + .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ + .wvr = addr & (~0x7ULL), + .details = { .vaddr = addr, .len = len } + }; + + if (cur_hw_wps >= max_hw_wps) { + return -ENOBUFS; + } + + /* + * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state, + * valid whether EL3 is implemented or not + */ + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3); + + switch (type) { + case GDB_WATCHPOINT_READ: + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1); + wp.details.flags = BP_MEM_READ; + break; + case GDB_WATCHPOINT_WRITE: + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2); + wp.details.flags = BP_MEM_WRITE; + break; + case GDB_WATCHPOINT_ACCESS: + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3); + wp.details.flags = BP_MEM_ACCESS; + break; + default: + g_assert_not_reached(); + break; + } + if (len <= 8) { + /* we align the address and set the bits in BAS */ + int off = addr & 0x7; + int bas = (1 << len) - 1; + + wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas); + } else { + /* For ranges above 8 bytes we need to be a power of 2 */ + if (is_power_of_2(len)) { + int bits = ctz64(len); + + wp.wvr &= ~((1 << bits) - 1); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff); + } else { + return -ENOBUFS; + } + } + + g_array_append_val(hw_watchpoints, wp); + return 0; +} + +bool check_watchpoint_in_range(int i, target_ulong addr) +{ + HWWatchpoint *wp = get_hw_wp(i); + uint64_t addr_top, addr_bottom = wp->wvr; + int bas = extract32(wp->wcr, 5, 8); + int mask = extract32(wp->wcr, 24, 4); + + if (mask) { + addr_top = addr_bottom + (1 << mask); + } else { + /* + * BAS must be contiguous but can offset against the base + * address in DBGWVR + */ + addr_bottom = addr_bottom + ctz32(bas); + addr_top = addr_bottom + clo32(bas); + } + + if (addr >= addr_bottom && addr <= addr_top) { + return true; + } + + return false; +} + +/** + * delete_hw_watchpoint() + * @addr: address of breakpoint + * + * Delete a breakpoint and shuffle any above down + */ + +int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type) +{ + int i; + for (i = 0; i < cur_hw_wps; i++) { + if (check_watchpoint_in_range(i, addr)) { + g_array_remove_index(hw_watchpoints, i); + return 0; + } + } + return -ENOENT; +} + +bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) +{ + int i; + + for (i = 0; i < cur_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + if (bp->bvr == pc) { + return true; + } + } + return false; +} + +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) +{ + int i; + + for (i = 0; i < cur_hw_wps; i++) { + if (check_watchpoint_in_range(i, addr)) { + return &get_hw_wp(i)->details; + } + } + return NULL; +} diff --git a/target/arm/internals.h b/target/arm/internals.h index c869d18c38..ce26299f7f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1447,4 +1447,54 @@ static inline bool arm_fgt_active(CPUARMState *env, int el) } void assert_hflags_rebuild_correctly(CPUARMState *env); + +/* + * Although the ARM implementation of hardware assisted debugging + * allows for different breakpoints per-core, the current GDB + * interface treats them as a global pool of registers (which seems to + * be the case for x86, ppc and s390). As a result we store one copy + * of registers which is used for all active cores. + * + * Write access is serialised by virtue of the GDB protocol which + * updates things. Read access (i.e. when the values are copied to the + * vCPU) is also gated by GDB's run control. + * + * This is not unreasonable as most of the time debugging kernels you + * never know which core will eventually execute your function. + */ + +typedef struct { + uint64_t bcr; + uint64_t bvr; +} HWBreakpoint; + +/* + * The watchpoint registers can cover more area than the requested + * watchpoint so we need to store the additional information + * somewhere. We also need to supply a CPUWatchpoint to the GDB stub + * when the watchpoint is hit. + */ +typedef struct { + uint64_t wcr; + uint64_t wvr; + CPUWatchpoint details; +} HWWatchpoint; + +/* Maximum and current break/watch point counts */ +extern int max_hw_bps, max_hw_wps; +extern GArray *hw_breakpoints, *hw_watchpoints; + +#define cur_hw_wps (hw_watchpoints->len) +#define cur_hw_bps (hw_breakpoints->len) +#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i)) +#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i)) + +bool find_hw_breakpoint(CPUState *cpu, target_ulong pc); +int insert_hw_breakpoint(target_ulong pc); +int delete_hw_breakpoint(target_ulong pc); + +bool check_watchpoint_in_range(int i, target_ulong addr); +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr); +int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type); +int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); #endif diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 810db33ccb..94bbd9661f 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -34,46 +34,6 @@ static bool have_guest_debug; -/* - * Although the ARM implementation of hardware assisted debugging - * allows for different breakpoints per-core, the current GDB - * interface treats them as a global pool of registers (which seems to - * be the case for x86, ppc and s390). As a result we store one copy - * of registers which is used for all active cores. - * - * Write access is serialised by virtue of the GDB protocol which - * updates things. Read access (i.e. when the values are copied to the - * vCPU) is also gated by GDB's run control. - * - * This is not unreasonable as most of the time debugging kernels you - * never know which core will eventually execute your function. - */ - -typedef struct { - uint64_t bcr; - uint64_t bvr; -} HWBreakpoint; - -/* The watchpoint registers can cover more area than the requested - * watchpoint so we need to store the additional information - * somewhere. We also need to supply a CPUWatchpoint to the GDB stub - * when the watchpoint is hit. - */ -typedef struct { - uint64_t wcr; - uint64_t wvr; - CPUWatchpoint details; -} HWWatchpoint; - -/* Maximum and current break/watch point counts */ -int max_hw_bps, max_hw_wps; -GArray *hw_breakpoints, *hw_watchpoints; - -#define cur_hw_wps (hw_watchpoints->len) -#define cur_hw_bps (hw_breakpoints->len) -#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i)) -#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i)) - void kvm_arm_init_debug(KVMState *s) { have_guest_debug = kvm_check_extension(s, @@ -89,217 +49,6 @@ void kvm_arm_init_debug(KVMState *s) return; } -/** - * insert_hw_breakpoint() - * @addr: address of breakpoint - * - * See ARM ARM D2.9.1 for details but here we are only going to create - * simple un-linked breakpoints (i.e. we don't chain breakpoints - * together to match address and context or vmid). The hardware is - * capable of fancier matching but that will require exposing that - * fanciness to GDB's interface - * - * DBGBCR_EL1, Debug Breakpoint Control Registers - * - * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0 - * +------+------+-------+-----+----+------+-----+------+-----+---+ - * | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E | - * +------+------+-------+-----+----+------+-----+------+-----+---+ - * - * BT: Breakpoint type (0 = unlinked address match) - * LBN: Linked BP number (0 = unused) - * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12) - * BAS: Byte Address Select (RES1 for AArch64) - * E: Enable bit - * - * DBGBVR_EL1, Debug Breakpoint Value Registers - * - * 63 53 52 49 48 2 1 0 - * +------+-----------+----------+-----+ - * | RESS | VA[52:49] | VA[48:2] | 0 0 | - * +------+-----------+----------+-----+ - * - * Depending on the addressing mode bits the top bits of the register - * are a sign extension of the highest applicable VA bit. Some - * versions of GDB don't do it correctly so we ensure they are correct - * here so future PC comparisons will work properly. - */ - -static int insert_hw_breakpoint(target_ulong addr) -{ - HWBreakpoint brk = { - .bcr = 0x1, /* BCR E=1, enable */ - .bvr = sextract64(addr, 0, 53) - }; - - if (cur_hw_bps >= max_hw_bps) { - return -ENOBUFS; - } - - brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */ - brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */ - - g_array_append_val(hw_breakpoints, brk); - - return 0; -} - -/** - * delete_hw_breakpoint() - * @pc: address of breakpoint - * - * Delete a breakpoint and shuffle any above down - */ - -static int delete_hw_breakpoint(target_ulong pc) -{ - int i; - for (i = 0; i < hw_breakpoints->len; i++) { - HWBreakpoint *brk = get_hw_bp(i); - if (brk->bvr == pc) { - g_array_remove_index(hw_breakpoints, i); - return 0; - } - } - return -ENOENT; -} - -/** - * insert_hw_watchpoint() - * @addr: address of watch point - * @len: size of area - * @type: type of watch point - * - * See ARM ARM D2.10. As with the breakpoints we can do some advanced - * stuff if we want to. The watch points can be linked with the break - * points above to make them context aware. However for simplicity - * currently we only deal with simple read/write watch points. - * - * D7.3.11 DBGWCR_EL1, Debug Watchpoint Control Registers - * - * 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0 - * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ - * | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E | - * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ - * - * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes)) - * WT: 0 - unlinked, 1 - linked (not currently used) - * LBN: Linked BP number (not currently used) - * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11) - * BAS: Byte Address Select - * LSC: Load/Store control (01: load, 10: store, 11: both) - * E: Enable - * - * The bottom 2 bits of the value register are masked. Therefore to - * break on any sizes smaller than an unaligned word you need to set - * MASK=0, BAS=bit per byte in question. For larger regions (^2) you - * need to ensure you mask the address as required and set BAS=0xff - */ - -static int insert_hw_watchpoint(target_ulong addr, - target_ulong len, int type) -{ - HWWatchpoint wp = { - .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ - .wvr = addr & (~0x7ULL), - .details = { .vaddr = addr, .len = len } - }; - - if (cur_hw_wps >= max_hw_wps) { - return -ENOBUFS; - } - - /* - * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state, - * valid whether EL3 is implemented or not - */ - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3); - - switch (type) { - case GDB_WATCHPOINT_READ: - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1); - wp.details.flags = BP_MEM_READ; - break; - case GDB_WATCHPOINT_WRITE: - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2); - wp.details.flags = BP_MEM_WRITE; - break; - case GDB_WATCHPOINT_ACCESS: - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3); - wp.details.flags = BP_MEM_ACCESS; - break; - default: - g_assert_not_reached(); - break; - } - if (len <= 8) { - /* we align the address and set the bits in BAS */ - int off = addr & 0x7; - int bas = (1 << len) - 1; - - wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas); - } else { - /* For ranges above 8 bytes we need to be a power of 2 */ - if (is_power_of_2(len)) { - int bits = ctz64(len); - - wp.wvr &= ~((1 << bits) - 1); - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits); - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff); - } else { - return -ENOBUFS; - } - } - - g_array_append_val(hw_watchpoints, wp); - return 0; -} - - -static bool check_watchpoint_in_range(int i, target_ulong addr) -{ - HWWatchpoint *wp = get_hw_wp(i); - uint64_t addr_top, addr_bottom = wp->wvr; - int bas = extract32(wp->wcr, 5, 8); - int mask = extract32(wp->wcr, 24, 4); - - if (mask) { - addr_top = addr_bottom + (1 << mask); - } else { - /* BAS must be contiguous but can offset against the base - * address in DBGWVR */ - addr_bottom = addr_bottom + ctz32(bas); - addr_top = addr_bottom + clo32(bas); - } - - if (addr >= addr_bottom && addr <= addr_top) { - return true; - } - - return false; -} - -/** - * delete_hw_watchpoint() - * @addr: address of breakpoint - * - * Delete a breakpoint and shuffle any above down - */ - -static int delete_hw_watchpoint(target_ulong addr, - target_ulong len, int type) -{ - int i; - for (i = 0; i < cur_hw_wps; i++) { - if (check_watchpoint_in_range(i, addr)) { - g_array_remove_index(hw_watchpoints, i); - return 0; - } - } - return -ENOENT; -} - - int kvm_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type) { @@ -364,31 +113,6 @@ bool kvm_arm_hw_debug_active(CPUState *cs) return ((cur_hw_wps > 0) || (cur_hw_bps > 0)); } -static bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) -{ - int i; - - for (i = 0; i < cur_hw_bps; i++) { - HWBreakpoint *bp = get_hw_bp(i); - if (bp->bvr == pc) { - return true; - } - } - return false; -} - -static CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) -{ - int i; - - for (i = 0; i < cur_hw_wps; i++) { - if (check_watchpoint_in_range(i, addr)) { - return &get_hw_wp(i)->details; - } - } - return NULL; -} - static bool kvm_arm_set_device_attr(CPUState *cs, struct kvm_device_attr *attr, const char *name) { diff --git a/target/arm/meson.build b/target/arm/meson.build index 359a649eaf..011e8ca113 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -8,7 +8,8 @@ arm_ss.add(files( )) arm_ss.add(zlib) -arm_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c')) +arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c')) +arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c', From ce799a04b2987e54a3f29b2139c9610ac8c467c9 Mon Sep 17 00:00:00 2001 From: Francesco Cagnin Date: Tue, 6 Jun 2023 10:19:29 +0100 Subject: [PATCH 133/412] hvf: handle access for more registers Required for guest debugging. Signed-off-by: Francesco Cagnin Message-id: 20230601153107.81955-3-fcagnin@quarkslab.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 213 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index ad65603445..e221e37055 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -107,6 +107,72 @@ #define SYSREG_ICC_SGI1R_EL1 SYSREG(3, 0, 12, 11, 5) #define SYSREG_ICC_SRE_EL1 SYSREG(3, 0, 12, 12, 5) +#define SYSREG_MDSCR_EL1 SYSREG(2, 0, 0, 2, 2) +#define SYSREG_DBGBVR0_EL1 SYSREG(2, 0, 0, 0, 4) +#define SYSREG_DBGBCR0_EL1 SYSREG(2, 0, 0, 0, 5) +#define SYSREG_DBGWVR0_EL1 SYSREG(2, 0, 0, 0, 6) +#define SYSREG_DBGWCR0_EL1 SYSREG(2, 0, 0, 0, 7) +#define SYSREG_DBGBVR1_EL1 SYSREG(2, 0, 0, 1, 4) +#define SYSREG_DBGBCR1_EL1 SYSREG(2, 0, 0, 1, 5) +#define SYSREG_DBGWVR1_EL1 SYSREG(2, 0, 0, 1, 6) +#define SYSREG_DBGWCR1_EL1 SYSREG(2, 0, 0, 1, 7) +#define SYSREG_DBGBVR2_EL1 SYSREG(2, 0, 0, 2, 4) +#define SYSREG_DBGBCR2_EL1 SYSREG(2, 0, 0, 2, 5) +#define SYSREG_DBGWVR2_EL1 SYSREG(2, 0, 0, 2, 6) +#define SYSREG_DBGWCR2_EL1 SYSREG(2, 0, 0, 2, 7) +#define SYSREG_DBGBVR3_EL1 SYSREG(2, 0, 0, 3, 4) +#define SYSREG_DBGBCR3_EL1 SYSREG(2, 0, 0, 3, 5) +#define SYSREG_DBGWVR3_EL1 SYSREG(2, 0, 0, 3, 6) +#define SYSREG_DBGWCR3_EL1 SYSREG(2, 0, 0, 3, 7) +#define SYSREG_DBGBVR4_EL1 SYSREG(2, 0, 0, 4, 4) +#define SYSREG_DBGBCR4_EL1 SYSREG(2, 0, 0, 4, 5) +#define SYSREG_DBGWVR4_EL1 SYSREG(2, 0, 0, 4, 6) +#define SYSREG_DBGWCR4_EL1 SYSREG(2, 0, 0, 4, 7) +#define SYSREG_DBGBVR5_EL1 SYSREG(2, 0, 0, 5, 4) +#define SYSREG_DBGBCR5_EL1 SYSREG(2, 0, 0, 5, 5) +#define SYSREG_DBGWVR5_EL1 SYSREG(2, 0, 0, 5, 6) +#define SYSREG_DBGWCR5_EL1 SYSREG(2, 0, 0, 5, 7) +#define SYSREG_DBGBVR6_EL1 SYSREG(2, 0, 0, 6, 4) +#define SYSREG_DBGBCR6_EL1 SYSREG(2, 0, 0, 6, 5) +#define SYSREG_DBGWVR6_EL1 SYSREG(2, 0, 0, 6, 6) +#define SYSREG_DBGWCR6_EL1 SYSREG(2, 0, 0, 6, 7) +#define SYSREG_DBGBVR7_EL1 SYSREG(2, 0, 0, 7, 4) +#define SYSREG_DBGBCR7_EL1 SYSREG(2, 0, 0, 7, 5) +#define SYSREG_DBGWVR7_EL1 SYSREG(2, 0, 0, 7, 6) +#define SYSREG_DBGWCR7_EL1 SYSREG(2, 0, 0, 7, 7) +#define SYSREG_DBGBVR8_EL1 SYSREG(2, 0, 0, 8, 4) +#define SYSREG_DBGBCR8_EL1 SYSREG(2, 0, 0, 8, 5) +#define SYSREG_DBGWVR8_EL1 SYSREG(2, 0, 0, 8, 6) +#define SYSREG_DBGWCR8_EL1 SYSREG(2, 0, 0, 8, 7) +#define SYSREG_DBGBVR9_EL1 SYSREG(2, 0, 0, 9, 4) +#define SYSREG_DBGBCR9_EL1 SYSREG(2, 0, 0, 9, 5) +#define SYSREG_DBGWVR9_EL1 SYSREG(2, 0, 0, 9, 6) +#define SYSREG_DBGWCR9_EL1 SYSREG(2, 0, 0, 9, 7) +#define SYSREG_DBGBVR10_EL1 SYSREG(2, 0, 0, 10, 4) +#define SYSREG_DBGBCR10_EL1 SYSREG(2, 0, 0, 10, 5) +#define SYSREG_DBGWVR10_EL1 SYSREG(2, 0, 0, 10, 6) +#define SYSREG_DBGWCR10_EL1 SYSREG(2, 0, 0, 10, 7) +#define SYSREG_DBGBVR11_EL1 SYSREG(2, 0, 0, 11, 4) +#define SYSREG_DBGBCR11_EL1 SYSREG(2, 0, 0, 11, 5) +#define SYSREG_DBGWVR11_EL1 SYSREG(2, 0, 0, 11, 6) +#define SYSREG_DBGWCR11_EL1 SYSREG(2, 0, 0, 11, 7) +#define SYSREG_DBGBVR12_EL1 SYSREG(2, 0, 0, 12, 4) +#define SYSREG_DBGBCR12_EL1 SYSREG(2, 0, 0, 12, 5) +#define SYSREG_DBGWVR12_EL1 SYSREG(2, 0, 0, 12, 6) +#define SYSREG_DBGWCR12_EL1 SYSREG(2, 0, 0, 12, 7) +#define SYSREG_DBGBVR13_EL1 SYSREG(2, 0, 0, 13, 4) +#define SYSREG_DBGBCR13_EL1 SYSREG(2, 0, 0, 13, 5) +#define SYSREG_DBGWVR13_EL1 SYSREG(2, 0, 0, 13, 6) +#define SYSREG_DBGWCR13_EL1 SYSREG(2, 0, 0, 13, 7) +#define SYSREG_DBGBVR14_EL1 SYSREG(2, 0, 0, 14, 4) +#define SYSREG_DBGBCR14_EL1 SYSREG(2, 0, 0, 14, 5) +#define SYSREG_DBGWVR14_EL1 SYSREG(2, 0, 0, 14, 6) +#define SYSREG_DBGWCR14_EL1 SYSREG(2, 0, 0, 14, 7) +#define SYSREG_DBGBVR15_EL1 SYSREG(2, 0, 0, 15, 4) +#define SYSREG_DBGBCR15_EL1 SYSREG(2, 0, 0, 15, 5) +#define SYSREG_DBGWVR15_EL1 SYSREG(2, 0, 0, 15, 6) +#define SYSREG_DBGWCR15_EL1 SYSREG(2, 0, 0, 15, 7) + #define WFX_IS_WFE (1 << 0) #define TMR_CTL_ENABLE (1 << 0) @@ -933,6 +999,78 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); } break; + case SYSREG_DBGBVR0_EL1: + case SYSREG_DBGBVR1_EL1: + case SYSREG_DBGBVR2_EL1: + case SYSREG_DBGBVR3_EL1: + case SYSREG_DBGBVR4_EL1: + case SYSREG_DBGBVR5_EL1: + case SYSREG_DBGBVR6_EL1: + case SYSREG_DBGBVR7_EL1: + case SYSREG_DBGBVR8_EL1: + case SYSREG_DBGBVR9_EL1: + case SYSREG_DBGBVR10_EL1: + case SYSREG_DBGBVR11_EL1: + case SYSREG_DBGBVR12_EL1: + case SYSREG_DBGBVR13_EL1: + case SYSREG_DBGBVR14_EL1: + case SYSREG_DBGBVR15_EL1: + val = env->cp15.dbgbvr[SYSREG_CRM(reg)]; + break; + case SYSREG_DBGBCR0_EL1: + case SYSREG_DBGBCR1_EL1: + case SYSREG_DBGBCR2_EL1: + case SYSREG_DBGBCR3_EL1: + case SYSREG_DBGBCR4_EL1: + case SYSREG_DBGBCR5_EL1: + case SYSREG_DBGBCR6_EL1: + case SYSREG_DBGBCR7_EL1: + case SYSREG_DBGBCR8_EL1: + case SYSREG_DBGBCR9_EL1: + case SYSREG_DBGBCR10_EL1: + case SYSREG_DBGBCR11_EL1: + case SYSREG_DBGBCR12_EL1: + case SYSREG_DBGBCR13_EL1: + case SYSREG_DBGBCR14_EL1: + case SYSREG_DBGBCR15_EL1: + val = env->cp15.dbgbcr[SYSREG_CRM(reg)]; + break; + case SYSREG_DBGWVR0_EL1: + case SYSREG_DBGWVR1_EL1: + case SYSREG_DBGWVR2_EL1: + case SYSREG_DBGWVR3_EL1: + case SYSREG_DBGWVR4_EL1: + case SYSREG_DBGWVR5_EL1: + case SYSREG_DBGWVR6_EL1: + case SYSREG_DBGWVR7_EL1: + case SYSREG_DBGWVR8_EL1: + case SYSREG_DBGWVR9_EL1: + case SYSREG_DBGWVR10_EL1: + case SYSREG_DBGWVR11_EL1: + case SYSREG_DBGWVR12_EL1: + case SYSREG_DBGWVR13_EL1: + case SYSREG_DBGWVR14_EL1: + case SYSREG_DBGWVR15_EL1: + val = env->cp15.dbgwvr[SYSREG_CRM(reg)]; + break; + case SYSREG_DBGWCR0_EL1: + case SYSREG_DBGWCR1_EL1: + case SYSREG_DBGWCR2_EL1: + case SYSREG_DBGWCR3_EL1: + case SYSREG_DBGWCR4_EL1: + case SYSREG_DBGWCR5_EL1: + case SYSREG_DBGWCR6_EL1: + case SYSREG_DBGWCR7_EL1: + case SYSREG_DBGWCR8_EL1: + case SYSREG_DBGWCR9_EL1: + case SYSREG_DBGWCR10_EL1: + case SYSREG_DBGWCR11_EL1: + case SYSREG_DBGWCR12_EL1: + case SYSREG_DBGWCR13_EL1: + case SYSREG_DBGWCR14_EL1: + case SYSREG_DBGWCR15_EL1: + val = env->cp15.dbgwcr[SYSREG_CRM(reg)]; + break; default: if (is_id_sysreg(reg)) { /* ID system registers read as RES0 */ @@ -1172,6 +1310,81 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); } break; + case SYSREG_MDSCR_EL1: + env->cp15.mdscr_el1 = val; + break; + case SYSREG_DBGBVR0_EL1: + case SYSREG_DBGBVR1_EL1: + case SYSREG_DBGBVR2_EL1: + case SYSREG_DBGBVR3_EL1: + case SYSREG_DBGBVR4_EL1: + case SYSREG_DBGBVR5_EL1: + case SYSREG_DBGBVR6_EL1: + case SYSREG_DBGBVR7_EL1: + case SYSREG_DBGBVR8_EL1: + case SYSREG_DBGBVR9_EL1: + case SYSREG_DBGBVR10_EL1: + case SYSREG_DBGBVR11_EL1: + case SYSREG_DBGBVR12_EL1: + case SYSREG_DBGBVR13_EL1: + case SYSREG_DBGBVR14_EL1: + case SYSREG_DBGBVR15_EL1: + env->cp15.dbgbvr[SYSREG_CRM(reg)] = val; + break; + case SYSREG_DBGBCR0_EL1: + case SYSREG_DBGBCR1_EL1: + case SYSREG_DBGBCR2_EL1: + case SYSREG_DBGBCR3_EL1: + case SYSREG_DBGBCR4_EL1: + case SYSREG_DBGBCR5_EL1: + case SYSREG_DBGBCR6_EL1: + case SYSREG_DBGBCR7_EL1: + case SYSREG_DBGBCR8_EL1: + case SYSREG_DBGBCR9_EL1: + case SYSREG_DBGBCR10_EL1: + case SYSREG_DBGBCR11_EL1: + case SYSREG_DBGBCR12_EL1: + case SYSREG_DBGBCR13_EL1: + case SYSREG_DBGBCR14_EL1: + case SYSREG_DBGBCR15_EL1: + env->cp15.dbgbcr[SYSREG_CRM(reg)] = val; + break; + case SYSREG_DBGWVR0_EL1: + case SYSREG_DBGWVR1_EL1: + case SYSREG_DBGWVR2_EL1: + case SYSREG_DBGWVR3_EL1: + case SYSREG_DBGWVR4_EL1: + case SYSREG_DBGWVR5_EL1: + case SYSREG_DBGWVR6_EL1: + case SYSREG_DBGWVR7_EL1: + case SYSREG_DBGWVR8_EL1: + case SYSREG_DBGWVR9_EL1: + case SYSREG_DBGWVR10_EL1: + case SYSREG_DBGWVR11_EL1: + case SYSREG_DBGWVR12_EL1: + case SYSREG_DBGWVR13_EL1: + case SYSREG_DBGWVR14_EL1: + case SYSREG_DBGWVR15_EL1: + env->cp15.dbgwvr[SYSREG_CRM(reg)] = val; + break; + case SYSREG_DBGWCR0_EL1: + case SYSREG_DBGWCR1_EL1: + case SYSREG_DBGWCR2_EL1: + case SYSREG_DBGWCR3_EL1: + case SYSREG_DBGWCR4_EL1: + case SYSREG_DBGWCR5_EL1: + case SYSREG_DBGWCR6_EL1: + case SYSREG_DBGWCR7_EL1: + case SYSREG_DBGWCR8_EL1: + case SYSREG_DBGWCR9_EL1: + case SYSREG_DBGWCR10_EL1: + case SYSREG_DBGWCR11_EL1: + case SYSREG_DBGWCR12_EL1: + case SYSREG_DBGWCR13_EL1: + case SYSREG_DBGWCR14_EL1: + case SYSREG_DBGWCR15_EL1: + env->cp15.dbgwcr[SYSREG_CRM(reg)] = val; + break; default: cpu_synchronize_state(cpu); trace_hvf_unhandled_sysreg_write(env->pc, reg, From f41520402c3a917c378ad166c2c76feb64608b09 Mon Sep 17 00:00:00 2001 From: Francesco Cagnin Date: Tue, 6 Jun 2023 10:19:30 +0100 Subject: [PATCH 134/412] hvf: add breakpoint handlers Required for guest debugging. The code has been structured like the KVM counterpart. Signed-off-by: Francesco Cagnin Message-id: 20230601153107.81955-4-fcagnin@quarkslab.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- accel/hvf/hvf-accel-ops.c | 109 ++++++++++++++++++++++++++++++++++++++ accel/hvf/hvf-all.c | 17 ++++++ include/sysemu/hvf.h | 22 ++++++++ include/sysemu/hvf_int.h | 1 + target/arm/hvf/hvf.c | 63 ++++++++++++++++++++++ target/i386/hvf/hvf.c | 24 +++++++++ 6 files changed, 236 insertions(+) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 24913ca9c4..92601b1369 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -52,6 +52,7 @@ #include "qemu/main-loop.h" #include "exec/address-spaces.h" #include "exec/exec-all.h" +#include "exec/gdbstub.h" #include "sysemu/cpus.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" @@ -334,6 +335,8 @@ static int hvf_accel_init(MachineState *ms) s->slots[x].slot_id = x; } + QTAILQ_INIT(&s->hvf_sw_breakpoints); + hvf_state = s; memory_listener_register(&hvf_memory_listener, &address_space_memory); @@ -462,6 +465,108 @@ static void hvf_start_vcpu_thread(CPUState *cpu) cpu, QEMU_THREAD_JOINABLE); } +static int hvf_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) +{ + struct hvf_sw_breakpoint *bp; + int err; + + if (type == GDB_BREAKPOINT_SW) { + bp = hvf_find_sw_breakpoint(cpu, addr); + if (bp) { + bp->use_count++; + return 0; + } + + bp = g_new(struct hvf_sw_breakpoint, 1); + bp->pc = addr; + bp->use_count = 1; + err = hvf_arch_insert_sw_breakpoint(cpu, bp); + if (err) { + g_free(bp); + return err; + } + + QTAILQ_INSERT_HEAD(&hvf_state->hvf_sw_breakpoints, bp, entry); + } else { + err = hvf_arch_insert_hw_breakpoint(addr, len, type); + if (err) { + return err; + } + } + + CPU_FOREACH(cpu) { + err = hvf_update_guest_debug(cpu); + if (err) { + return err; + } + } + return 0; +} + +static int hvf_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) +{ + struct hvf_sw_breakpoint *bp; + int err; + + if (type == GDB_BREAKPOINT_SW) { + bp = hvf_find_sw_breakpoint(cpu, addr); + if (!bp) { + return -ENOENT; + } + + if (bp->use_count > 1) { + bp->use_count--; + return 0; + } + + err = hvf_arch_remove_sw_breakpoint(cpu, bp); + if (err) { + return err; + } + + QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry); + g_free(bp); + } else { + err = hvf_arch_remove_hw_breakpoint(addr, len, type); + if (err) { + return err; + } + } + + CPU_FOREACH(cpu) { + err = hvf_update_guest_debug(cpu); + if (err) { + return err; + } + } + return 0; +} + +static void hvf_remove_all_breakpoints(CPUState *cpu) +{ + struct hvf_sw_breakpoint *bp, *next; + CPUState *tmpcpu; + + QTAILQ_FOREACH_SAFE(bp, &hvf_state->hvf_sw_breakpoints, entry, next) { + if (hvf_arch_remove_sw_breakpoint(cpu, bp) != 0) { + /* Try harder to find a CPU that currently sees the breakpoint. */ + CPU_FOREACH(tmpcpu) + { + if (hvf_arch_remove_sw_breakpoint(tmpcpu, bp) == 0) { + break; + } + } + } + QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry); + g_free(bp); + } + hvf_arch_remove_all_hw_breakpoints(); + + CPU_FOREACH(cpu) { + hvf_update_guest_debug(cpu); + } +} + static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); @@ -473,6 +578,10 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) ops->synchronize_post_init = hvf_cpu_synchronize_post_init; ops->synchronize_state = hvf_cpu_synchronize_state; ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm; + + ops->insert_breakpoint = hvf_insert_breakpoint; + ops->remove_breakpoint = hvf_remove_breakpoint; + ops->remove_all_breakpoints = hvf_remove_all_breakpoints; }; static const TypeInfo hvf_accel_ops_type = { .name = ACCEL_OPS_NAME("hvf"), diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 0043f4d308..e983c23ad7 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -44,3 +44,20 @@ void assert_hvf_ok(hv_return_t ret) abort(); } + +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, target_ulong pc) +{ + struct hvf_sw_breakpoint *bp; + + QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) { + if (bp->pc == pc) { + return bp; + } + } + return NULL; +} + +int hvf_sw_breakpoints_active(CPUState *cpu) +{ + return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); +} diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index bb70082e45..386020a29c 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -17,6 +17,7 @@ #include "qom/object.h" #ifdef NEED_CPU_H +#include "cpu.h" #ifdef CONFIG_HVF uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, @@ -36,4 +37,25 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) +#ifdef NEED_CPU_H +struct hvf_sw_breakpoint { + target_ulong pc; + target_ulong saved_insn; + int use_count; + QTAILQ_ENTRY(hvf_sw_breakpoint) entry; +}; + +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, + target_ulong pc); +int hvf_sw_breakpoints_active(CPUState *cpu); + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, + int type); +int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, + int type); +void hvf_arch_remove_all_hw_breakpoints(void); +#endif /* NEED_CPU_H */ + #endif diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 6545f7cd61..3592239fdc 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -45,6 +45,7 @@ struct HVFState { hvf_vcpu_caps *hvf_caps; uint64_t vtimer_offset; + QTAILQ_HEAD(, hvf_sw_breakpoint) hvf_sw_breakpoints; }; extern HVFState *hvf_state; diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index e221e37055..bb83627727 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -31,6 +31,8 @@ #include "trace/trace-target_arm_hvf.h" #include "migration/vmstate.h" +#include "exec/gdbstub.h" + #define HVF_SYSREG(crn, crm, op0, op1, op2) \ ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) #define PL1_WRITE_MASK 0x4 @@ -1711,3 +1713,64 @@ int hvf_arch_init(void) qemu_add_vm_change_state_handler(hvf_vm_state_change, &vtimer); return 0; } + +static const uint32_t brk_insn = 0xd4200000; + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || + cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { + return -EINVAL; + } + return 0; +} + +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + static uint32_t brk; + + if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&brk, 4, 0) || + brk != brk_insn || + cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + return -EINVAL; + } + return 0; +} + +int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return insert_hw_breakpoint(addr); + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return insert_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return delete_hw_breakpoint(addr); + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return delete_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +void hvf_arch_remove_all_hw_breakpoints(void) +{ + if (cur_hw_wps > 0) { + g_array_remove_range(hw_watchpoints, 0, cur_hw_wps); + } + if (cur_hw_bps > 0) { + g_array_remove_range(hw_breakpoints, 0, cur_hw_bps); + } +} diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 8d2248bb3f..08bc96ecbc 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -679,3 +679,27 @@ int hvf_vcpu_exec(CPUState *cpu) return ret; } + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + return -ENOSYS; +} + +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + return -ENOSYS; +} + +int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type) +{ + return -ENOSYS; +} + +int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type) +{ + return -ENOSYS; +} + +void hvf_arch_remove_all_hw_breakpoints(void) +{ +} From eb2edc42b12683fdbe54003db7701f7ab6cda036 Mon Sep 17 00:00:00 2001 From: Francesco Cagnin Date: Tue, 6 Jun 2023 10:19:30 +0100 Subject: [PATCH 135/412] hvf: add guest debugging handlers for Apple Silicon hosts Guests can now be debugged through the gdbstub. Support is added for single-stepping, software breakpoints, hardware breakpoints and watchpoints. The code has been structured like the KVM counterpart. While guest debugging is enabled, the guest can still read and write the DBG*_EL1 registers but they don't have any effect. Signed-off-by: Francesco Cagnin Message-id: 20230601153107.81955-5-fcagnin@quarkslab.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- accel/hvf/hvf-accel-ops.c | 10 + accel/hvf/hvf-all.c | 6 + include/sysemu/hvf.h | 15 ++ include/sysemu/hvf_int.h | 1 + target/arm/hvf/hvf.c | 474 +++++++++++++++++++++++++++++++++++++- target/arm/hvf_arm.h | 7 + target/i386/hvf/hvf.c | 9 + 7 files changed, 520 insertions(+), 2 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 92601b1369..9c3da03c94 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -343,12 +343,18 @@ static int hvf_accel_init(MachineState *ms) return hvf_arch_init(); } +static inline int hvf_gdbstub_sstep_flags(void) +{ + return SSTEP_ENABLE | SSTEP_NOIRQ; +} + static void hvf_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "HVF"; ac->init_machine = hvf_accel_init; ac->allowed = &hvf_allowed; + ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; } static const TypeInfo hvf_accel_type = { @@ -398,6 +404,8 @@ static int hvf_init_vcpu(CPUState *cpu) cpu->vcpu_dirty = 1; assert_hvf_ok(r); + cpu->hvf->guest_debug_enabled = false; + return hvf_arch_init_vcpu(cpu); } @@ -582,6 +590,8 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) ops->insert_breakpoint = hvf_insert_breakpoint; ops->remove_breakpoint = hvf_remove_breakpoint; ops->remove_all_breakpoints = hvf_remove_all_breakpoints; + ops->update_guest_debug = hvf_update_guest_debug; + ops->supports_guest_debug = hvf_arch_supports_guest_debug; }; static const TypeInfo hvf_accel_ops_type = { .name = ACCEL_OPS_NAME("hvf"), diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index e983c23ad7..754707dbfb 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -61,3 +61,9 @@ int hvf_sw_breakpoints_active(CPUState *cpu) { return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); } + +int hvf_update_guest_debug(CPUState *cpu) +{ + hvf_arch_update_guest_debug(cpu); + return 0; +} diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index 386020a29c..70549b9158 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -56,6 +56,21 @@ int hvf_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type); void hvf_arch_remove_all_hw_breakpoints(void); + +/* + * hvf_update_guest_debug: + * @cs: CPUState for the CPU to update + * + * Update guest to enable or disable debugging. Per-arch specifics will be + * handled by calling down to hvf_arch_update_guest_debug. + */ +int hvf_update_guest_debug(CPUState *cpu); +void hvf_arch_update_guest_debug(CPUState *cpu); + +/* + * Return whether the guest supports debugging. + */ +bool hvf_arch_supports_guest_debug(void); #endif /* NEED_CPU_H */ #endif diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 3592239fdc..6ab119e49f 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -54,6 +54,7 @@ struct hvf_vcpu_state { void *exit; bool vtimer_masked; sigset_t unblock_ipi_mask; + bool guest_debug_enabled; }; void assert_hvf_ok(hv_return_t ret); diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index bb83627727..2bd0a35cba 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -33,6 +33,116 @@ #include "exec/gdbstub.h" +#define MDSCR_EL1_SS_SHIFT 0 +#define MDSCR_EL1_MDE_SHIFT 15 + +static uint16_t dbgbcr_regs[] = { + HV_SYS_REG_DBGBCR0_EL1, + HV_SYS_REG_DBGBCR1_EL1, + HV_SYS_REG_DBGBCR2_EL1, + HV_SYS_REG_DBGBCR3_EL1, + HV_SYS_REG_DBGBCR4_EL1, + HV_SYS_REG_DBGBCR5_EL1, + HV_SYS_REG_DBGBCR6_EL1, + HV_SYS_REG_DBGBCR7_EL1, + HV_SYS_REG_DBGBCR8_EL1, + HV_SYS_REG_DBGBCR9_EL1, + HV_SYS_REG_DBGBCR10_EL1, + HV_SYS_REG_DBGBCR11_EL1, + HV_SYS_REG_DBGBCR12_EL1, + HV_SYS_REG_DBGBCR13_EL1, + HV_SYS_REG_DBGBCR14_EL1, + HV_SYS_REG_DBGBCR15_EL1, +}; +static uint16_t dbgbvr_regs[] = { + HV_SYS_REG_DBGBVR0_EL1, + HV_SYS_REG_DBGBVR1_EL1, + HV_SYS_REG_DBGBVR2_EL1, + HV_SYS_REG_DBGBVR3_EL1, + HV_SYS_REG_DBGBVR4_EL1, + HV_SYS_REG_DBGBVR5_EL1, + HV_SYS_REG_DBGBVR6_EL1, + HV_SYS_REG_DBGBVR7_EL1, + HV_SYS_REG_DBGBVR8_EL1, + HV_SYS_REG_DBGBVR9_EL1, + HV_SYS_REG_DBGBVR10_EL1, + HV_SYS_REG_DBGBVR11_EL1, + HV_SYS_REG_DBGBVR12_EL1, + HV_SYS_REG_DBGBVR13_EL1, + HV_SYS_REG_DBGBVR14_EL1, + HV_SYS_REG_DBGBVR15_EL1, +}; +static uint16_t dbgwcr_regs[] = { + HV_SYS_REG_DBGWCR0_EL1, + HV_SYS_REG_DBGWCR1_EL1, + HV_SYS_REG_DBGWCR2_EL1, + HV_SYS_REG_DBGWCR3_EL1, + HV_SYS_REG_DBGWCR4_EL1, + HV_SYS_REG_DBGWCR5_EL1, + HV_SYS_REG_DBGWCR6_EL1, + HV_SYS_REG_DBGWCR7_EL1, + HV_SYS_REG_DBGWCR8_EL1, + HV_SYS_REG_DBGWCR9_EL1, + HV_SYS_REG_DBGWCR10_EL1, + HV_SYS_REG_DBGWCR11_EL1, + HV_SYS_REG_DBGWCR12_EL1, + HV_SYS_REG_DBGWCR13_EL1, + HV_SYS_REG_DBGWCR14_EL1, + HV_SYS_REG_DBGWCR15_EL1, +}; +static uint16_t dbgwvr_regs[] = { + HV_SYS_REG_DBGWVR0_EL1, + HV_SYS_REG_DBGWVR1_EL1, + HV_SYS_REG_DBGWVR2_EL1, + HV_SYS_REG_DBGWVR3_EL1, + HV_SYS_REG_DBGWVR4_EL1, + HV_SYS_REG_DBGWVR5_EL1, + HV_SYS_REG_DBGWVR6_EL1, + HV_SYS_REG_DBGWVR7_EL1, + HV_SYS_REG_DBGWVR8_EL1, + HV_SYS_REG_DBGWVR9_EL1, + HV_SYS_REG_DBGWVR10_EL1, + HV_SYS_REG_DBGWVR11_EL1, + HV_SYS_REG_DBGWVR12_EL1, + HV_SYS_REG_DBGWVR13_EL1, + HV_SYS_REG_DBGWVR14_EL1, + HV_SYS_REG_DBGWVR15_EL1, +}; + +static inline int hvf_arm_num_brps(hv_vcpu_config_t config) +{ + uint64_t val; + hv_return_t ret; + ret = hv_vcpu_config_get_feature_reg(config, HV_FEATURE_REG_ID_AA64DFR0_EL1, + &val); + assert_hvf_ok(ret); + return FIELD_EX64(val, ID_AA64DFR0, BRPS) + 1; +} + +static inline int hvf_arm_num_wrps(hv_vcpu_config_t config) +{ + uint64_t val; + hv_return_t ret; + ret = hv_vcpu_config_get_feature_reg(config, HV_FEATURE_REG_ID_AA64DFR0_EL1, + &val); + assert_hvf_ok(ret); + return FIELD_EX64(val, ID_AA64DFR0, WRPS) + 1; +} + +void hvf_arm_init_debug(void) +{ + hv_vcpu_config_t config; + config = hv_vcpu_config_create(); + + max_hw_bps = hvf_arm_num_brps(config); + hw_breakpoints = + g_array_sized_new(true, true, sizeof(HWBreakpoint), max_hw_bps); + + max_hw_wps = hvf_arm_num_wrps(config); + hw_watchpoints = + g_array_sized_new(true, true, sizeof(HWWatchpoint), max_hw_wps); +} + #define HVF_SYSREG(crn, crm, op0, op1, op2) \ ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) #define PL1_WRITE_MASK 0x4 @@ -465,6 +575,92 @@ int hvf_get_registers(CPUState *cpu) continue; } + if (cpu->hvf->guest_debug_enabled) { + /* Handle debug registers */ + switch (hvf_sreg_match[i].reg) { + case HV_SYS_REG_DBGBVR0_EL1: + case HV_SYS_REG_DBGBCR0_EL1: + case HV_SYS_REG_DBGWVR0_EL1: + case HV_SYS_REG_DBGWCR0_EL1: + case HV_SYS_REG_DBGBVR1_EL1: + case HV_SYS_REG_DBGBCR1_EL1: + case HV_SYS_REG_DBGWVR1_EL1: + case HV_SYS_REG_DBGWCR1_EL1: + case HV_SYS_REG_DBGBVR2_EL1: + case HV_SYS_REG_DBGBCR2_EL1: + case HV_SYS_REG_DBGWVR2_EL1: + case HV_SYS_REG_DBGWCR2_EL1: + case HV_SYS_REG_DBGBVR3_EL1: + case HV_SYS_REG_DBGBCR3_EL1: + case HV_SYS_REG_DBGWVR3_EL1: + case HV_SYS_REG_DBGWCR3_EL1: + case HV_SYS_REG_DBGBVR4_EL1: + case HV_SYS_REG_DBGBCR4_EL1: + case HV_SYS_REG_DBGWVR4_EL1: + case HV_SYS_REG_DBGWCR4_EL1: + case HV_SYS_REG_DBGBVR5_EL1: + case HV_SYS_REG_DBGBCR5_EL1: + case HV_SYS_REG_DBGWVR5_EL1: + case HV_SYS_REG_DBGWCR5_EL1: + case HV_SYS_REG_DBGBVR6_EL1: + case HV_SYS_REG_DBGBCR6_EL1: + case HV_SYS_REG_DBGWVR6_EL1: + case HV_SYS_REG_DBGWCR6_EL1: + case HV_SYS_REG_DBGBVR7_EL1: + case HV_SYS_REG_DBGBCR7_EL1: + case HV_SYS_REG_DBGWVR7_EL1: + case HV_SYS_REG_DBGWCR7_EL1: + case HV_SYS_REG_DBGBVR8_EL1: + case HV_SYS_REG_DBGBCR8_EL1: + case HV_SYS_REG_DBGWVR8_EL1: + case HV_SYS_REG_DBGWCR8_EL1: + case HV_SYS_REG_DBGBVR9_EL1: + case HV_SYS_REG_DBGBCR9_EL1: + case HV_SYS_REG_DBGWVR9_EL1: + case HV_SYS_REG_DBGWCR9_EL1: + case HV_SYS_REG_DBGBVR10_EL1: + case HV_SYS_REG_DBGBCR10_EL1: + case HV_SYS_REG_DBGWVR10_EL1: + case HV_SYS_REG_DBGWCR10_EL1: + case HV_SYS_REG_DBGBVR11_EL1: + case HV_SYS_REG_DBGBCR11_EL1: + case HV_SYS_REG_DBGWVR11_EL1: + case HV_SYS_REG_DBGWCR11_EL1: + case HV_SYS_REG_DBGBVR12_EL1: + case HV_SYS_REG_DBGBCR12_EL1: + case HV_SYS_REG_DBGWVR12_EL1: + case HV_SYS_REG_DBGWCR12_EL1: + case HV_SYS_REG_DBGBVR13_EL1: + case HV_SYS_REG_DBGBCR13_EL1: + case HV_SYS_REG_DBGWVR13_EL1: + case HV_SYS_REG_DBGWCR13_EL1: + case HV_SYS_REG_DBGBVR14_EL1: + case HV_SYS_REG_DBGBCR14_EL1: + case HV_SYS_REG_DBGWVR14_EL1: + case HV_SYS_REG_DBGWCR14_EL1: + case HV_SYS_REG_DBGBVR15_EL1: + case HV_SYS_REG_DBGBCR15_EL1: + case HV_SYS_REG_DBGWVR15_EL1: + case HV_SYS_REG_DBGWCR15_EL1: { + /* + * If the guest is being debugged, the vCPU's debug registers + * are holding the gdbstub's view of the registers (set in + * hvf_arch_update_guest_debug()). + * Since the environment is used to store only the guest's view + * of the registers, don't update it with the values from the + * vCPU but simply keep the values from the previous + * environment. + */ + const ARMCPRegInfo *ri; + ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_sreg_match[i].key); + val = read_raw_cp_reg(env, ri); + + arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; + continue; + } + } + } + ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, &val); assert_hvf_ok(ret); @@ -516,6 +712,82 @@ int hvf_put_registers(CPUState *cpu) continue; } + if (cpu->hvf->guest_debug_enabled) { + /* Handle debug registers */ + switch (hvf_sreg_match[i].reg) { + case HV_SYS_REG_DBGBVR0_EL1: + case HV_SYS_REG_DBGBCR0_EL1: + case HV_SYS_REG_DBGWVR0_EL1: + case HV_SYS_REG_DBGWCR0_EL1: + case HV_SYS_REG_DBGBVR1_EL1: + case HV_SYS_REG_DBGBCR1_EL1: + case HV_SYS_REG_DBGWVR1_EL1: + case HV_SYS_REG_DBGWCR1_EL1: + case HV_SYS_REG_DBGBVR2_EL1: + case HV_SYS_REG_DBGBCR2_EL1: + case HV_SYS_REG_DBGWVR2_EL1: + case HV_SYS_REG_DBGWCR2_EL1: + case HV_SYS_REG_DBGBVR3_EL1: + case HV_SYS_REG_DBGBCR3_EL1: + case HV_SYS_REG_DBGWVR3_EL1: + case HV_SYS_REG_DBGWCR3_EL1: + case HV_SYS_REG_DBGBVR4_EL1: + case HV_SYS_REG_DBGBCR4_EL1: + case HV_SYS_REG_DBGWVR4_EL1: + case HV_SYS_REG_DBGWCR4_EL1: + case HV_SYS_REG_DBGBVR5_EL1: + case HV_SYS_REG_DBGBCR5_EL1: + case HV_SYS_REG_DBGWVR5_EL1: + case HV_SYS_REG_DBGWCR5_EL1: + case HV_SYS_REG_DBGBVR6_EL1: + case HV_SYS_REG_DBGBCR6_EL1: + case HV_SYS_REG_DBGWVR6_EL1: + case HV_SYS_REG_DBGWCR6_EL1: + case HV_SYS_REG_DBGBVR7_EL1: + case HV_SYS_REG_DBGBCR7_EL1: + case HV_SYS_REG_DBGWVR7_EL1: + case HV_SYS_REG_DBGWCR7_EL1: + case HV_SYS_REG_DBGBVR8_EL1: + case HV_SYS_REG_DBGBCR8_EL1: + case HV_SYS_REG_DBGWVR8_EL1: + case HV_SYS_REG_DBGWCR8_EL1: + case HV_SYS_REG_DBGBVR9_EL1: + case HV_SYS_REG_DBGBCR9_EL1: + case HV_SYS_REG_DBGWVR9_EL1: + case HV_SYS_REG_DBGWCR9_EL1: + case HV_SYS_REG_DBGBVR10_EL1: + case HV_SYS_REG_DBGBCR10_EL1: + case HV_SYS_REG_DBGWVR10_EL1: + case HV_SYS_REG_DBGWCR10_EL1: + case HV_SYS_REG_DBGBVR11_EL1: + case HV_SYS_REG_DBGBCR11_EL1: + case HV_SYS_REG_DBGWVR11_EL1: + case HV_SYS_REG_DBGWCR11_EL1: + case HV_SYS_REG_DBGBVR12_EL1: + case HV_SYS_REG_DBGBCR12_EL1: + case HV_SYS_REG_DBGWVR12_EL1: + case HV_SYS_REG_DBGWCR12_EL1: + case HV_SYS_REG_DBGBVR13_EL1: + case HV_SYS_REG_DBGBCR13_EL1: + case HV_SYS_REG_DBGWVR13_EL1: + case HV_SYS_REG_DBGWCR13_EL1: + case HV_SYS_REG_DBGBVR14_EL1: + case HV_SYS_REG_DBGBCR14_EL1: + case HV_SYS_REG_DBGWVR14_EL1: + case HV_SYS_REG_DBGWCR14_EL1: + case HV_SYS_REG_DBGBVR15_EL1: + case HV_SYS_REG_DBGBCR15_EL1: + case HV_SYS_REG_DBGWVR15_EL1: + case HV_SYS_REG_DBGWCR15_EL1: + /* + * If the guest is being debugged, the vCPU's debug registers + * are already holding the gdbstub's view of the registers (set + * in hvf_arch_update_guest_debug()). + */ + continue; + } + } + val = arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx]; ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, val); assert_hvf_ok(ret); @@ -1532,11 +1804,13 @@ int hvf_vcpu_exec(CPUState *cpu) { ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; + int ret; hv_vcpu_exit_t *hvf_exit = cpu->hvf->exit; hv_return_t r; bool advance_pc = false; - if (hvf_inject_interrupts(cpu)) { + if (!(cpu->singlestep_enabled & SSTEP_NOIRQ) && + hvf_inject_interrupts(cpu)) { return EXCP_INTERRUPT; } @@ -1554,6 +1828,7 @@ int hvf_vcpu_exec(CPUState *cpu) uint64_t syndrome = hvf_exit->exception.syndrome; uint32_t ec = syn_get_ec(syndrome); + ret = 0; qemu_mutex_lock_iothread(); switch (exit_reason) { case HV_EXIT_REASON_EXCEPTION: @@ -1573,6 +1848,49 @@ int hvf_vcpu_exec(CPUState *cpu) hvf_sync_vtimer(cpu); switch (ec) { + case EC_SOFTWARESTEP: { + ret = EXCP_DEBUG; + + if (!cpu->singlestep_enabled) { + error_report("EC_SOFTWARESTEP but single-stepping not enabled"); + } + break; + } + case EC_AA64_BKPT: { + ret = EXCP_DEBUG; + + cpu_synchronize_state(cpu); + + if (!hvf_find_sw_breakpoint(cpu, env->pc)) { + /* Re-inject into the guest */ + ret = 0; + hvf_raise_exception(cpu, EXCP_BKPT, syn_aa64_bkpt(0)); + } + break; + } + case EC_BREAKPOINT: { + ret = EXCP_DEBUG; + + cpu_synchronize_state(cpu); + + if (!find_hw_breakpoint(cpu, env->pc)) { + error_report("EC_BREAKPOINT but unknown hw breakpoint"); + } + break; + } + case EC_WATCHPOINT: { + ret = EXCP_DEBUG; + + cpu_synchronize_state(cpu); + + CPUWatchpoint *wp = + find_hw_watchpoint(cpu, hvf_exit->exception.virtual_address); + if (!wp) { + error_report("EXCP_DEBUG but unknown hw watchpoint"); + } + cpu->watchpoint_hit = wp; + break; + } case EC_DATAABORT: { bool isv = syndrome & ARM_EL_ISV; bool iswrite = (syndrome >> 6) & 1; @@ -1677,9 +1995,14 @@ int hvf_vcpu_exec(CPUState *cpu) pc += 4; r = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_PC, pc); assert_hvf_ok(r); + + /* Handle single-stepping over instructions which trigger a VM exit */ + if (cpu->singlestep_enabled) { + ret = EXCP_DEBUG; + } } - return 0; + return ret; } static const VMStateDescription vmstate_hvf_vtimer = { @@ -1711,6 +2034,9 @@ int hvf_arch_init(void) hvf_state->vtimer_offset = mach_absolute_time(); vmstate_register(NULL, 0, &vmstate_hvf_vtimer, &vtimer); qemu_add_vm_change_state_handler(hvf_vm_state_change, &vtimer); + + hvf_arm_init_debug(); + return 0; } @@ -1774,3 +2100,147 @@ void hvf_arch_remove_all_hw_breakpoints(void) g_array_remove_range(hw_breakpoints, 0, cur_hw_bps); } } + +/* + * Update the vCPU with the gdbstub's view of debug registers. This view + * consists of all hardware breakpoints and watchpoints inserted so far while + * debugging the guest. + */ +static void hvf_put_gdbstub_debug_registers(CPUState *cpu) +{ + hv_return_t r = HV_SUCCESS; + int i; + + for (i = 0; i < cur_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbcr_regs[i], bp->bcr); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbvr_regs[i], bp->bvr); + assert_hvf_ok(r); + } + for (i = cur_hw_bps; i < max_hw_bps; i++) { + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbcr_regs[i], 0); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbvr_regs[i], 0); + assert_hvf_ok(r); + } + + for (i = 0; i < cur_hw_wps; i++) { + HWWatchpoint *wp = get_hw_wp(i); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwcr_regs[i], wp->wcr); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwvr_regs[i], wp->wvr); + assert_hvf_ok(r); + } + for (i = cur_hw_wps; i < max_hw_wps; i++) { + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwcr_regs[i], 0); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwvr_regs[i], 0); + assert_hvf_ok(r); + } +} + +/* + * Update the vCPU with the guest's view of debug registers. This view is kept + * in the environment at all times. + */ +static void hvf_put_guest_debug_registers(CPUState *cpu) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + hv_return_t r = HV_SUCCESS; + int i; + + for (i = 0; i < max_hw_bps; i++) { + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbcr_regs[i], + env->cp15.dbgbcr[i]); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgbvr_regs[i], + env->cp15.dbgbvr[i]); + assert_hvf_ok(r); + } + + for (i = 0; i < max_hw_wps; i++) { + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwcr_regs[i], + env->cp15.dbgwcr[i]); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->hvf->fd, dbgwvr_regs[i], + env->cp15.dbgwvr[i]); + assert_hvf_ok(r); + } +} + +static inline bool hvf_arm_hw_debug_active(CPUState *cpu) +{ + return ((cur_hw_wps > 0) || (cur_hw_bps > 0)); +} + +static void hvf_arch_set_traps(void) +{ + CPUState *cpu; + bool should_enable_traps = false; + hv_return_t r = HV_SUCCESS; + + /* Check whether guest debugging is enabled for at least one vCPU; if it + * is, enable exiting the guest on all vCPUs */ + CPU_FOREACH(cpu) { + should_enable_traps |= cpu->hvf->guest_debug_enabled; + } + CPU_FOREACH(cpu) { + /* Set whether debug exceptions exit the guest */ + r = hv_vcpu_set_trap_debug_exceptions(cpu->hvf->fd, + should_enable_traps); + assert_hvf_ok(r); + + /* Set whether accesses to debug registers exit the guest */ + r = hv_vcpu_set_trap_debug_reg_accesses(cpu->hvf->fd, + should_enable_traps); + assert_hvf_ok(r); + } +} + +void hvf_arch_update_guest_debug(CPUState *cpu) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + + /* Check whether guest debugging is enabled */ + cpu->hvf->guest_debug_enabled = cpu->singlestep_enabled || + hvf_sw_breakpoints_active(cpu) || + hvf_arm_hw_debug_active(cpu); + + /* Update debug registers */ + if (cpu->hvf->guest_debug_enabled) { + hvf_put_gdbstub_debug_registers(cpu); + } else { + hvf_put_guest_debug_registers(cpu); + } + + cpu_synchronize_state(cpu); + + /* Enable/disable single-stepping */ + if (cpu->singlestep_enabled) { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_SS_SHIFT, 1, 1); + pstate_write(env, pstate_read(env) | PSTATE_SS); + } else { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_SS_SHIFT, 1, 0); + } + + /* Enable/disable Breakpoint exceptions */ + if (hvf_arm_hw_debug_active(cpu)) { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 1); + } else { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 0); + } + + hvf_arch_set_traps(); +} + +inline bool hvf_arch_supports_guest_debug(void) +{ + return true; +} diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index 9a9d1a0bf5..e848c1d27d 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -13,6 +13,13 @@ #include "cpu.h" +/** + * hvf_arm_init_debug() - initialize guest debug capabilities + * + * Should be called only once before using guest debug capabilities. + */ +void hvf_arm_init_debug(void); + void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); #endif diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 08bc96ecbc..f6775c942a 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -703,3 +703,12 @@ int hvf_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type) void hvf_arch_remove_all_hw_breakpoints(void) { } + +void hvf_arch_update_guest_debug(CPUState *cpu) +{ +} + +inline bool hvf_arch_supports_guest_debug(void) +{ + return false; +} From 32dbebcc7e375fef47834ac3951009468294db01 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Tue, 6 Jun 2023 10:19:30 +0100 Subject: [PATCH 136/412] hw/net/can: Introduce Xilinx Versal CANFD controller The Xilinx Versal CANFD controller is developed based on SocketCAN, QEMU CAN bus implementation. Bus connection and socketCAN connection for each CAN module can be set through command lines. Signed-off-by: Vikram Garhwal Reviewed-by: Francisco Iglesias Signed-off-by: Peter Maydell --- hw/net/can/meson.build | 1 + hw/net/can/trace-events | 7 + hw/net/can/xlnx-versal-canfd.c | 2107 ++++++++++++++++++++++++++++ include/hw/net/xlnx-versal-canfd.h | 87 ++ 4 files changed, 2202 insertions(+) create mode 100644 hw/net/can/xlnx-versal-canfd.c create mode 100644 include/hw/net/xlnx-versal-canfd.h diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build index 8fabbd9ee6..8d85201cb0 100644 --- a/hw/net/can/meson.build +++ b/hw/net/can/meson.build @@ -5,3 +5,4 @@ softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c')) softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c')) softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c')) softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c')) diff --git a/hw/net/can/trace-events b/hw/net/can/trace-events index 8346a98ab5..de64ac1b31 100644 --- a/hw/net/can/trace-events +++ b/hw/net/can/trace-events @@ -7,3 +7,10 @@ xlnx_can_filter_mask_pre_write(uint8_t filter_num, uint32_t value) "Filter%d MAS xlnx_can_tx_data(uint32_t id, uint8_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x" xlnx_can_rx_data(uint32_t id, uint32_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x" xlnx_can_rx_discard(uint32_t status) "Controller is not enabled for bus communication. Status Register: 0x%08x" + +# xlnx-versal-canfd.c +xlnx_canfd_update_irq(char *path, uint32_t isr, uint32_t ier, uint32_t irq) "%s: ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x" +xlnx_canfd_rx_fifo_filter_reject(char *path, uint32_t id, uint8_t dlc) "%s: Frame: ID: 0x%08x DLC: 0x%02x" +xlnx_canfd_rx_data(char *path, uint32_t id, uint8_t dlc, uint8_t flags) "%s: Frame: ID: 0x%08x DLC: 0x%02x CANFD Flag: 0x%02x" +xlnx_canfd_tx_data(char *path, uint32_t id, uint8_t dlc, uint8_t flgas) "%s: Frame: ID: 0x%08x DLC: 0x%02x CANFD Flag: 0x%02x" +xlnx_canfd_reset(char *path, uint32_t val) "%s: Resetting controller with value = 0x%08x" diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c new file mode 100644 index 0000000000..5b8ce0a285 --- /dev/null +++ b/hw/net/can/xlnx-versal-canfd.c @@ -0,0 +1,2107 @@ +/* + * QEMU model of the Xilinx Versal CANFD device. + * + * This implementation is based on the following datasheet: + * https://docs.xilinx.com/v/u/2.0-English/pg223-canfd + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written-by: Vikram Garhwal + * + * Based on QEMU CANFD Device emulation implemented by Jin Yang, Deniz Eren and + * Pavel Pisa + * + * 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 "hw/irq.h" +#include "hw/register.h" +#include "qapi/error.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/cutils.h" +#include "qemu/event_notifier.h" +#include "hw/qdev-properties.h" +#include "qom/object_interfaces.h" +#include "migration/vmstate.h" +#include "hw/net/xlnx-versal-canfd.h" +#include "trace.h" + +REG32(SOFTWARE_RESET_REGISTER, 0x0) + FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1) + FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1) +REG32(MODE_SELECT_REGISTER, 0x4) + FIELD(MODE_SELECT_REGISTER, ITO, 8, 8) + FIELD(MODE_SELECT_REGISTER, ABR, 7, 1) + FIELD(MODE_SELECT_REGISTER, SBR, 6, 1) + FIELD(MODE_SELECT_REGISTER, DPEE, 5, 1) + FIELD(MODE_SELECT_REGISTER, DAR, 4, 1) + FIELD(MODE_SELECT_REGISTER, BRSD, 3, 1) + FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1) + FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1) + FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1) +REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8) + FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8) +REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc) + FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 16, 7) + FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 8, 7) + FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 8) +REG32(ERROR_COUNTER_REGISTER, 0x10) + FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8) + FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8) +REG32(ERROR_STATUS_REGISTER, 0x14) + FIELD(ERROR_STATUS_REGISTER, F_BERR, 11, 1) + FIELD(ERROR_STATUS_REGISTER, F_STER, 10, 1) + FIELD(ERROR_STATUS_REGISTER, F_FMER, 9, 1) + FIELD(ERROR_STATUS_REGISTER, F_CRCER, 8, 1) + FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1) + FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1) + FIELD(ERROR_STATUS_REGISTER, STER, 2, 1) + FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1) + FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1) +REG32(STATUS_REGISTER, 0x18) + FIELD(STATUS_REGISTER, TDCV, 16, 7) + FIELD(STATUS_REGISTER, SNOOP, 12, 1) + FIELD(STATUS_REGISTER, BSFR_CONFIG, 10, 1) + FIELD(STATUS_REGISTER, PEE_CONFIG, 9, 1) + FIELD(STATUS_REGISTER, ESTAT, 7, 2) + FIELD(STATUS_REGISTER, ERRWRN, 6, 1) + FIELD(STATUS_REGISTER, BBSY, 5, 1) + FIELD(STATUS_REGISTER, BIDLE, 4, 1) + FIELD(STATUS_REGISTER, NORMAL, 3, 1) + FIELD(STATUS_REGISTER, SLEEP, 2, 1) + FIELD(STATUS_REGISTER, LBACK, 1, 1) + FIELD(STATUS_REGISTER, CONFIG, 0, 1) +REG32(INTERRUPT_STATUS_REGISTER, 0x1c) + FIELD(INTERRUPT_STATUS_REGISTER, TXEWMFLL, 31, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXEOFLW, 30, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXBOFLW_BI, 24, 6) + FIELD(INTERRUPT_STATUS_REGISTER, RXLRM_BI, 18, 6) + FIELD(INTERRUPT_STATUS_REGISTER, RXMNF, 17, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL_1, 16, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFOFLW_1, 15, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXCRS, 14, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXRRS, 13, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1) + FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1) + FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1) + FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1) + /* + * In the original HW description below bit is named as ERROR but an ERROR + * field name collides with a macro in Windows build. To avoid Windows build + * failures, the bit is renamed to ERROR_BIT. + */ + FIELD(INTERRUPT_STATUS_REGISTER, ERROR_BIT, 8, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFOFLW, 6, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TSCNT_OFLW, 5, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1) + FIELD(INTERRUPT_STATUS_REGISTER, BSFRD, 3, 1) + FIELD(INTERRUPT_STATUS_REGISTER, PEE, 2, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1) + FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1) +REG32(INTERRUPT_ENABLE_REGISTER, 0x20) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXEWMFLL, 31, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXEOFLW, 30, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXMNF, 17, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL_1, 16, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXFOFLW_1, 15, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXCRS, 14, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXRRS, 13, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERFXOFLW, 6, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETSCNT_OFLW, 5, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EBSFRD, 3, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EPEE, 2, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EARBLOST, 0, 1) +REG32(INTERRUPT_CLEAR_REGISTER, 0x24) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXEWMFLL, 31, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXEOFLW, 30, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXMNF, 17, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL_1, 16, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXFOFLW_1, 15, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXCRS, 14, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXRRS, 13, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRFXOFLW, 6, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTSCNT_OFLW, 5, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CBSFRD, 3, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CPEE, 2, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CARBLOST, 0, 1) +REG32(TIMESTAMP_REGISTER, 0x28) + FIELD(TIMESTAMP_REGISTER, TIMESTAMP_CNT, 16, 16) + FIELD(TIMESTAMP_REGISTER, CTS, 0, 1) +REG32(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x88) + FIELD(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, TDC, 16, 1) + FIELD(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, TDCOFF, 8, 6) + FIELD(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, DP_BRP, 0, 8) +REG32(DATA_PHASE_BIT_TIMING_REGISTER, 0x8c) + FIELD(DATA_PHASE_BIT_TIMING_REGISTER, DP_SJW, 16, 4) + FIELD(DATA_PHASE_BIT_TIMING_REGISTER, DP_TS2, 8, 4) + FIELD(DATA_PHASE_BIT_TIMING_REGISTER, DP_TS1, 0, 5) +REG32(TX_BUFFER_READY_REQUEST_REGISTER, 0x90) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR31, 31, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR30, 30, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR29, 29, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR28, 28, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR27, 27, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR26, 26, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR25, 25, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR24, 24, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR23, 23, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR22, 22, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR21, 21, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR20, 20, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR19, 19, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR18, 18, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR17, 17, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR16, 16, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR15, 15, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR14, 14, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR13, 13, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR12, 12, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR11, 11, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR10, 10, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR9, 9, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR8, 8, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR7, 7, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR6, 6, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR5, 5, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR4, 4, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR3, 3, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR2, 2, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR1, 1, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR0, 0, 1) +REG32(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, 0x94) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS31, 31, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS30, 30, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS29, 29, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS28, 28, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS27, 27, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS26, 26, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS25, 25, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS24, 24, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS23, 23, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS22, 22, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS21, 21, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS20, 20, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS19, 19, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS18, 18, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS17, 17, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS16, 16, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS15, 15, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS14, 14, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS13, 13, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS12, 12, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS11, 11, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS10, 10, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS9, 9, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS8, 8, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS7, 7, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS6, 6, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS5, 5, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS4, 4, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS3, 3, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS2, 2, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS1, 1, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS0, 0, 1) +REG32(TX_BUFFER_CANCEL_REQUEST_REGISTER, 0x98) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR31, 31, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR30, 30, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR29, 29, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR28, 28, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR27, 27, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR26, 26, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR25, 25, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR24, 24, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR23, 23, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR22, 22, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR21, 21, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR20, 20, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR19, 19, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR18, 18, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR17, 17, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR16, 16, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR15, 15, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR14, 14, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR13, 13, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR12, 12, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR11, 11, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR10, 10, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR9, 9, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR8, 8, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR7, 7, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR6, 6, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR5, 5, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR4, 4, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR3, 3, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR2, 2, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR1, 1, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR0, 0, 1) +REG32(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, 0x9c) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS31, 31, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS30, 30, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS29, 29, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS28, 28, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS27, 27, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS26, 26, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS25, 25, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS24, 24, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS23, 23, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS22, 22, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS21, 21, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS20, 20, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS19, 19, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS18, 18, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS17, 17, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS16, 16, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS15, 15, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS14, 14, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS13, 13, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS12, 12, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS11, 11, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS10, 10, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS9, 9, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS8, 8, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS7, 7, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS6, 6, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS5, 5, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS4, 4, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS3, 3, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS2, 2, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS1, 1, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS0, 0, 1) +REG32(TX_EVENT_FIFO_STATUS_REGISTER, 0xa0) + FIELD(TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL, 8, 6) + FIELD(TX_EVENT_FIFO_STATUS_REGISTER, TXE_IRI, 7, 1) + FIELD(TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, 0, 5) +REG32(TX_EVENT_FIFO_WATERMARK_REGISTER, 0xa4) + FIELD(TX_EVENT_FIFO_WATERMARK_REGISTER, TXE_FWM, 0, 5) +REG32(ACCEPTANCE_FILTER_CONTROL_REGISTER, 0xe0) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF31, 31, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF30, 30, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF29, 29, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF28, 28, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF27, 27, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF26, 26, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF25, 25, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF24, 24, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF23, 23, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF22, 22, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF21, 21, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF20, 20, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF19, 19, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF18, 18, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF17, 17, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF16, 16, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF15, 15, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF14, 14, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF13, 13, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF12, 12, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF11, 11, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF10, 10, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF9, 9, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF8, 8, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF7, 7, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF6, 6, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF5, 5, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF4, 4, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF3, 3, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF2, 2, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF1, 1, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF0, 0, 1) +REG32(RX_FIFO_STATUS_REGISTER, 0xe8) + FIELD(RX_FIFO_STATUS_REGISTER, FL_1, 24, 7) + FIELD(RX_FIFO_STATUS_REGISTER, IRI_1, 23, 1) + FIELD(RX_FIFO_STATUS_REGISTER, RI_1, 16, 6) + FIELD(RX_FIFO_STATUS_REGISTER, FL, 8, 7) + FIELD(RX_FIFO_STATUS_REGISTER, IRI, 7, 1) + FIELD(RX_FIFO_STATUS_REGISTER, RI, 0, 6) +REG32(RX_FIFO_WATERMARK_REGISTER, 0xec) + FIELD(RX_FIFO_WATERMARK_REGISTER, RXFP, 16, 5) + FIELD(RX_FIFO_WATERMARK_REGISTER, RXFWM_1, 8, 6) + FIELD(RX_FIFO_WATERMARK_REGISTER, RXFWM, 0, 6) +REG32(TB_ID_REGISTER, 0x100) + FIELD(TB_ID_REGISTER, ID, 21, 11) + FIELD(TB_ID_REGISTER, SRR_RTR_RRS, 20, 1) + FIELD(TB_ID_REGISTER, IDE, 19, 1) + FIELD(TB_ID_REGISTER, ID_EXT, 1, 18) + FIELD(TB_ID_REGISTER, RTR_RRS, 0, 1) +REG32(TB0_DLC_REGISTER, 0x104) + FIELD(TB0_DLC_REGISTER, DLC, 28, 4) + FIELD(TB0_DLC_REGISTER, FDF, 27, 1) + FIELD(TB0_DLC_REGISTER, BRS, 26, 1) + FIELD(TB0_DLC_REGISTER, RSVD2, 25, 1) + FIELD(TB0_DLC_REGISTER, EFC, 24, 1) + FIELD(TB0_DLC_REGISTER, MM, 16, 8) + FIELD(TB0_DLC_REGISTER, RSVD1, 0, 16) +REG32(TB_DW0_REGISTER, 0x108) + FIELD(TB_DW0_REGISTER, DATA_BYTES0, 24, 8) + FIELD(TB_DW0_REGISTER, DATA_BYTES1, 16, 8) + FIELD(TB_DW0_REGISTER, DATA_BYTES2, 8, 8) + FIELD(TB_DW0_REGISTER, DATA_BYTES3, 0, 8) +REG32(TB_DW1_REGISTER, 0x10c) + FIELD(TB_DW1_REGISTER, DATA_BYTES4, 24, 8) + FIELD(TB_DW1_REGISTER, DATA_BYTES5, 16, 8) + FIELD(TB_DW1_REGISTER, DATA_BYTES6, 8, 8) + FIELD(TB_DW1_REGISTER, DATA_BYTES7, 0, 8) +REG32(TB_DW2_REGISTER, 0x110) + FIELD(TB_DW2_REGISTER, DATA_BYTES8, 24, 8) + FIELD(TB_DW2_REGISTER, DATA_BYTES9, 16, 8) + FIELD(TB_DW2_REGISTER, DATA_BYTES10, 8, 8) + FIELD(TB_DW2_REGISTER, DATA_BYTES11, 0, 8) +REG32(TB_DW3_REGISTER, 0x114) + FIELD(TB_DW3_REGISTER, DATA_BYTES12, 24, 8) + FIELD(TB_DW3_REGISTER, DATA_BYTES13, 16, 8) + FIELD(TB_DW3_REGISTER, DATA_BYTES14, 8, 8) + FIELD(TB_DW3_REGISTER, DATA_BYTES15, 0, 8) +REG32(TB_DW4_REGISTER, 0x118) + FIELD(TB_DW4_REGISTER, DATA_BYTES16, 24, 8) + FIELD(TB_DW4_REGISTER, DATA_BYTES17, 16, 8) + FIELD(TB_DW4_REGISTER, DATA_BYTES18, 8, 8) + FIELD(TB_DW4_REGISTER, DATA_BYTES19, 0, 8) +REG32(TB_DW5_REGISTER, 0x11c) + FIELD(TB_DW5_REGISTER, DATA_BYTES20, 24, 8) + FIELD(TB_DW5_REGISTER, DATA_BYTES21, 16, 8) + FIELD(TB_DW5_REGISTER, DATA_BYTES22, 8, 8) + FIELD(TB_DW5_REGISTER, DATA_BYTES23, 0, 8) +REG32(TB_DW6_REGISTER, 0x120) + FIELD(TB_DW6_REGISTER, DATA_BYTES24, 24, 8) + FIELD(TB_DW6_REGISTER, DATA_BYTES25, 16, 8) + FIELD(TB_DW6_REGISTER, DATA_BYTES26, 8, 8) + FIELD(TB_DW6_REGISTER, DATA_BYTES27, 0, 8) +REG32(TB_DW7_REGISTER, 0x124) + FIELD(TB_DW7_REGISTER, DATA_BYTES28, 24, 8) + FIELD(TB_DW7_REGISTER, DATA_BYTES29, 16, 8) + FIELD(TB_DW7_REGISTER, DATA_BYTES30, 8, 8) + FIELD(TB_DW7_REGISTER, DATA_BYTES31, 0, 8) +REG32(TB_DW8_REGISTER, 0x128) + FIELD(TB_DW8_REGISTER, DATA_BYTES32, 24, 8) + FIELD(TB_DW8_REGISTER, DATA_BYTES33, 16, 8) + FIELD(TB_DW8_REGISTER, DATA_BYTES34, 8, 8) + FIELD(TB_DW8_REGISTER, DATA_BYTES35, 0, 8) +REG32(TB_DW9_REGISTER, 0x12c) + FIELD(TB_DW9_REGISTER, DATA_BYTES36, 24, 8) + FIELD(TB_DW9_REGISTER, DATA_BYTES37, 16, 8) + FIELD(TB_DW9_REGISTER, DATA_BYTES38, 8, 8) + FIELD(TB_DW9_REGISTER, DATA_BYTES39, 0, 8) +REG32(TB_DW10_REGISTER, 0x130) + FIELD(TB_DW10_REGISTER, DATA_BYTES40, 24, 8) + FIELD(TB_DW10_REGISTER, DATA_BYTES41, 16, 8) + FIELD(TB_DW10_REGISTER, DATA_BYTES42, 8, 8) + FIELD(TB_DW10_REGISTER, DATA_BYTES43, 0, 8) +REG32(TB_DW11_REGISTER, 0x134) + FIELD(TB_DW11_REGISTER, DATA_BYTES44, 24, 8) + FIELD(TB_DW11_REGISTER, DATA_BYTES45, 16, 8) + FIELD(TB_DW11_REGISTER, DATA_BYTES46, 8, 8) + FIELD(TB_DW11_REGISTER, DATA_BYTES47, 0, 8) +REG32(TB_DW12_REGISTER, 0x138) + FIELD(TB_DW12_REGISTER, DATA_BYTES48, 24, 8) + FIELD(TB_DW12_REGISTER, DATA_BYTES49, 16, 8) + FIELD(TB_DW12_REGISTER, DATA_BYTES50, 8, 8) + FIELD(TB_DW12_REGISTER, DATA_BYTES51, 0, 8) +REG32(TB_DW13_REGISTER, 0x13c) + FIELD(TB_DW13_REGISTER, DATA_BYTES52, 24, 8) + FIELD(TB_DW13_REGISTER, DATA_BYTES53, 16, 8) + FIELD(TB_DW13_REGISTER, DATA_BYTES54, 8, 8) + FIELD(TB_DW13_REGISTER, DATA_BYTES55, 0, 8) +REG32(TB_DW14_REGISTER, 0x140) + FIELD(TB_DW14_REGISTER, DATA_BYTES56, 24, 8) + FIELD(TB_DW14_REGISTER, DATA_BYTES57, 16, 8) + FIELD(TB_DW14_REGISTER, DATA_BYTES58, 8, 8) + FIELD(TB_DW14_REGISTER, DATA_BYTES59, 0, 8) +REG32(TB_DW15_REGISTER, 0x144) + FIELD(TB_DW15_REGISTER, DATA_BYTES60, 24, 8) + FIELD(TB_DW15_REGISTER, DATA_BYTES61, 16, 8) + FIELD(TB_DW15_REGISTER, DATA_BYTES62, 8, 8) + FIELD(TB_DW15_REGISTER, DATA_BYTES63, 0, 8) +REG32(AFMR_REGISTER, 0xa00) + FIELD(AFMR_REGISTER, AMID, 21, 11) + FIELD(AFMR_REGISTER, AMSRR, 20, 1) + FIELD(AFMR_REGISTER, AMIDE, 19, 1) + FIELD(AFMR_REGISTER, AMID_EXT, 1, 18) + FIELD(AFMR_REGISTER, AMRTR, 0, 1) +REG32(AFIR_REGISTER, 0xa04) + FIELD(AFIR_REGISTER, AIID, 21, 11) + FIELD(AFIR_REGISTER, AISRR, 20, 1) + FIELD(AFIR_REGISTER, AIIDE, 19, 1) + FIELD(AFIR_REGISTER, AIID_EXT, 1, 18) + FIELD(AFIR_REGISTER, AIRTR, 0, 1) +REG32(TXE_FIFO_TB_ID_REGISTER, 0x2000) + FIELD(TXE_FIFO_TB_ID_REGISTER, ID, 21, 11) + FIELD(TXE_FIFO_TB_ID_REGISTER, SRR_RTR_RRS, 20, 1) + FIELD(TXE_FIFO_TB_ID_REGISTER, IDE, 19, 1) + FIELD(TXE_FIFO_TB_ID_REGISTER, ID_EXT, 1, 18) + FIELD(TXE_FIFO_TB_ID_REGISTER, RTR_RRS, 0, 1) +REG32(TXE_FIFO_TB_DLC_REGISTER, 0x2004) + FIELD(TXE_FIFO_TB_DLC_REGISTER, DLC, 28, 4) + FIELD(TXE_FIFO_TB_DLC_REGISTER, FDF, 27, 1) + FIELD(TXE_FIFO_TB_DLC_REGISTER, BRS, 26, 1) + FIELD(TXE_FIFO_TB_DLC_REGISTER, ET, 24, 2) + FIELD(TXE_FIFO_TB_DLC_REGISTER, MM, 16, 8) + FIELD(TXE_FIFO_TB_DLC_REGISTER, TIMESTAMP, 0, 16) +REG32(RB_ID_REGISTER, 0x2100) + FIELD(RB_ID_REGISTER, ID, 21, 11) + FIELD(RB_ID_REGISTER, SRR_RTR_RRS, 20, 1) + FIELD(RB_ID_REGISTER, IDE, 19, 1) + FIELD(RB_ID_REGISTER, ID_EXT, 1, 18) + FIELD(RB_ID_REGISTER, RTR_RRS, 0, 1) +REG32(RB_DLC_REGISTER, 0x2104) + FIELD(RB_DLC_REGISTER, DLC, 28, 4) + FIELD(RB_DLC_REGISTER, FDF, 27, 1) + FIELD(RB_DLC_REGISTER, BRS, 26, 1) + FIELD(RB_DLC_REGISTER, ESI, 25, 1) + FIELD(RB_DLC_REGISTER, MATCHED_FILTER_INDEX, 16, 5) + FIELD(RB_DLC_REGISTER, TIMESTAMP, 0, 16) +REG32(RB_DW0_REGISTER, 0x2108) + FIELD(RB_DW0_REGISTER, DATA_BYTES0, 24, 8) + FIELD(RB_DW0_REGISTER, DATA_BYTES1, 16, 8) + FIELD(RB_DW0_REGISTER, DATA_BYTES2, 8, 8) + FIELD(RB_DW0_REGISTER, DATA_BYTES3, 0, 8) +REG32(RB_DW1_REGISTER, 0x210c) + FIELD(RB_DW1_REGISTER, DATA_BYTES4, 24, 8) + FIELD(RB_DW1_REGISTER, DATA_BYTES5, 16, 8) + FIELD(RB_DW1_REGISTER, DATA_BYTES6, 8, 8) + FIELD(RB_DW1_REGISTER, DATA_BYTES7, 0, 8) +REG32(RB_DW2_REGISTER, 0x2110) + FIELD(RB_DW2_REGISTER, DATA_BYTES8, 24, 8) + FIELD(RB_DW2_REGISTER, DATA_BYTES9, 16, 8) + FIELD(RB_DW2_REGISTER, DATA_BYTES10, 8, 8) + FIELD(RB_DW2_REGISTER, DATA_BYTES11, 0, 8) +REG32(RB_DW3_REGISTER, 0x2114) + FIELD(RB_DW3_REGISTER, DATA_BYTES12, 24, 8) + FIELD(RB_DW3_REGISTER, DATA_BYTES13, 16, 8) + FIELD(RB_DW3_REGISTER, DATA_BYTES14, 8, 8) + FIELD(RB_DW3_REGISTER, DATA_BYTES15, 0, 8) +REG32(RB_DW4_REGISTER, 0x2118) + FIELD(RB_DW4_REGISTER, DATA_BYTES16, 24, 8) + FIELD(RB_DW4_REGISTER, DATA_BYTES17, 16, 8) + FIELD(RB_DW4_REGISTER, DATA_BYTES18, 8, 8) + FIELD(RB_DW4_REGISTER, DATA_BYTES19, 0, 8) +REG32(RB_DW5_REGISTER, 0x211c) + FIELD(RB_DW5_REGISTER, DATA_BYTES20, 24, 8) + FIELD(RB_DW5_REGISTER, DATA_BYTES21, 16, 8) + FIELD(RB_DW5_REGISTER, DATA_BYTES22, 8, 8) + FIELD(RB_DW5_REGISTER, DATA_BYTES23, 0, 8) +REG32(RB_DW6_REGISTER, 0x2120) + FIELD(RB_DW6_REGISTER, DATA_BYTES24, 24, 8) + FIELD(RB_DW6_REGISTER, DATA_BYTES25, 16, 8) + FIELD(RB_DW6_REGISTER, DATA_BYTES26, 8, 8) + FIELD(RB_DW6_REGISTER, DATA_BYTES27, 0, 8) +REG32(RB_DW7_REGISTER, 0x2124) + FIELD(RB_DW7_REGISTER, DATA_BYTES28, 24, 8) + FIELD(RB_DW7_REGISTER, DATA_BYTES29, 16, 8) + FIELD(RB_DW7_REGISTER, DATA_BYTES30, 8, 8) + FIELD(RB_DW7_REGISTER, DATA_BYTES31, 0, 8) +REG32(RB_DW8_REGISTER, 0x2128) + FIELD(RB_DW8_REGISTER, DATA_BYTES32, 24, 8) + FIELD(RB_DW8_REGISTER, DATA_BYTES33, 16, 8) + FIELD(RB_DW8_REGISTER, DATA_BYTES34, 8, 8) + FIELD(RB_DW8_REGISTER, DATA_BYTES35, 0, 8) +REG32(RB_DW9_REGISTER, 0x212c) + FIELD(RB_DW9_REGISTER, DATA_BYTES36, 24, 8) + FIELD(RB_DW9_REGISTER, DATA_BYTES37, 16, 8) + FIELD(RB_DW9_REGISTER, DATA_BYTES38, 8, 8) + FIELD(RB_DW9_REGISTER, DATA_BYTES39, 0, 8) +REG32(RB_DW10_REGISTER, 0x2130) + FIELD(RB_DW10_REGISTER, DATA_BYTES40, 24, 8) + FIELD(RB_DW10_REGISTER, DATA_BYTES41, 16, 8) + FIELD(RB_DW10_REGISTER, DATA_BYTES42, 8, 8) + FIELD(RB_DW10_REGISTER, DATA_BYTES43, 0, 8) +REG32(RB_DW11_REGISTER, 0x2134) + FIELD(RB_DW11_REGISTER, DATA_BYTES44, 24, 8) + FIELD(RB_DW11_REGISTER, DATA_BYTES45, 16, 8) + FIELD(RB_DW11_REGISTER, DATA_BYTES46, 8, 8) + FIELD(RB_DW11_REGISTER, DATA_BYTES47, 0, 8) +REG32(RB_DW12_REGISTER, 0x2138) + FIELD(RB_DW12_REGISTER, DATA_BYTES48, 24, 8) + FIELD(RB_DW12_REGISTER, DATA_BYTES49, 16, 8) + FIELD(RB_DW12_REGISTER, DATA_BYTES50, 8, 8) + FIELD(RB_DW12_REGISTER, DATA_BYTES51, 0, 8) +REG32(RB_DW13_REGISTER, 0x213c) + FIELD(RB_DW13_REGISTER, DATA_BYTES52, 24, 8) + FIELD(RB_DW13_REGISTER, DATA_BYTES53, 16, 8) + FIELD(RB_DW13_REGISTER, DATA_BYTES54, 8, 8) + FIELD(RB_DW13_REGISTER, DATA_BYTES55, 0, 8) +REG32(RB_DW14_REGISTER, 0x2140) + FIELD(RB_DW14_REGISTER, DATA_BYTES56, 24, 8) + FIELD(RB_DW14_REGISTER, DATA_BYTES57, 16, 8) + FIELD(RB_DW14_REGISTER, DATA_BYTES58, 8, 8) + FIELD(RB_DW14_REGISTER, DATA_BYTES59, 0, 8) +REG32(RB_DW15_REGISTER, 0x2144) + FIELD(RB_DW15_REGISTER, DATA_BYTES60, 24, 8) + FIELD(RB_DW15_REGISTER, DATA_BYTES61, 16, 8) + FIELD(RB_DW15_REGISTER, DATA_BYTES62, 8, 8) + FIELD(RB_DW15_REGISTER, DATA_BYTES63, 0, 8) +REG32(RB_ID_REGISTER_1, 0x4100) + FIELD(RB_ID_REGISTER_1, ID, 21, 11) + FIELD(RB_ID_REGISTER_1, SRR_RTR_RRS, 20, 1) + FIELD(RB_ID_REGISTER_1, IDE, 19, 1) + FIELD(RB_ID_REGISTER_1, ID_EXT, 1, 18) + FIELD(RB_ID_REGISTER_1, RTR_RRS, 0, 1) +REG32(RB_DLC_REGISTER_1, 0x4104) + FIELD(RB_DLC_REGISTER_1, DLC, 28, 4) + FIELD(RB_DLC_REGISTER_1, FDF, 27, 1) + FIELD(RB_DLC_REGISTER_1, BRS, 26, 1) + FIELD(RB_DLC_REGISTER_1, ESI, 25, 1) + FIELD(RB_DLC_REGISTER_1, MATCHED_FILTER_INDEX, 16, 5) + FIELD(RB_DLC_REGISTER_1, TIMESTAMP, 0, 16) +REG32(RB0_DW0_REGISTER_1, 0x4108) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES0, 24, 8) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES1, 16, 8) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES2, 8, 8) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES3, 0, 8) +REG32(RB_DW1_REGISTER_1, 0x410c) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES4, 24, 8) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES5, 16, 8) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES6, 8, 8) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES7, 0, 8) +REG32(RB_DW2_REGISTER_1, 0x4110) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES8, 24, 8) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES9, 16, 8) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES10, 8, 8) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES11, 0, 8) +REG32(RB_DW3_REGISTER_1, 0x4114) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES12, 24, 8) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES13, 16, 8) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES14, 8, 8) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES15, 0, 8) +REG32(RB_DW4_REGISTER_1, 0x4118) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES16, 24, 8) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES17, 16, 8) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES18, 8, 8) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES19, 0, 8) +REG32(RB_DW5_REGISTER_1, 0x411c) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES20, 24, 8) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES21, 16, 8) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES22, 8, 8) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES23, 0, 8) +REG32(RB_DW6_REGISTER_1, 0x4120) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES24, 24, 8) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES25, 16, 8) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES26, 8, 8) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES27, 0, 8) +REG32(RB_DW7_REGISTER_1, 0x4124) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES28, 24, 8) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES29, 16, 8) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES30, 8, 8) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES31, 0, 8) +REG32(RB_DW8_REGISTER_1, 0x4128) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES32, 24, 8) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES33, 16, 8) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES34, 8, 8) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES35, 0, 8) +REG32(RB_DW9_REGISTER_1, 0x412c) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES36, 24, 8) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES37, 16, 8) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES38, 8, 8) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES39, 0, 8) +REG32(RB_DW10_REGISTER_1, 0x4130) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES40, 24, 8) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES41, 16, 8) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES42, 8, 8) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES43, 0, 8) +REG32(RB_DW11_REGISTER_1, 0x4134) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES44, 24, 8) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES45, 16, 8) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES46, 8, 8) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES47, 0, 8) +REG32(RB_DW12_REGISTER_1, 0x4138) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES48, 24, 8) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES49, 16, 8) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES50, 8, 8) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES51, 0, 8) +REG32(RB_DW13_REGISTER_1, 0x413c) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES52, 24, 8) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES53, 16, 8) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES54, 8, 8) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES55, 0, 8) +REG32(RB_DW14_REGISTER_1, 0x4140) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES56, 24, 8) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES57, 16, 8) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES58, 8, 8) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES59, 0, 8) +REG32(RB_DW15_REGISTER_1, 0x4144) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES60, 24, 8) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES61, 16, 8) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES62, 8, 8) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES63, 0, 8) + +static uint8_t canfd_dlc_array[8] = {8, 12, 16, 20, 24, 32, 48, 64}; + +static void canfd_update_irq(XlnxVersalCANFDState *s) +{ + unsigned int irq = s->regs[R_INTERRUPT_STATUS_REGISTER] & + s->regs[R_INTERRUPT_ENABLE_REGISTER]; + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + /* RX watermark interrupts. */ + if (ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, FL) > + ARRAY_FIELD_EX32(s->regs, RX_FIFO_WATERMARK_REGISTER, RXFWM)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1); + } + + if (ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, FL_1) > + ARRAY_FIELD_EX32(s->regs, RX_FIFO_WATERMARK_REGISTER, RXFWM_1)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL_1, 1); + } + + /* TX watermark interrupt. */ + if (ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL) > + ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_WATERMARK_REGISTER, TXE_FWM)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXEWMFLL, 1); + } + + trace_xlnx_canfd_update_irq(path, s->regs[R_INTERRUPT_STATUS_REGISTER], + s->regs[R_INTERRUPT_ENABLE_REGISTER], irq); + + qemu_set_irq(s->irq_canfd_int, irq); +} + +static void canfd_ier_post_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + canfd_update_irq(s); +} + +static uint64_t canfd_icr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val; + + /* + * RXBOFLW_BI field is automatically cleared to default if RXBOFLW bit is + * cleared in ISR. + */ + if (ARRAY_FIELD_EX32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL_1)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXBOFLW_BI, 0); + } + + canfd_update_irq(s); + + return 0; +} + +static void canfd_config_reset(XlnxVersalCANFDState *s) +{ + + unsigned int i; + + /* Reset all the configuration registers. */ + for (i = 0; i < R_RX_FIFO_WATERMARK_REGISTER; ++i) { + register_reset(&s->reg_info[i]); + } + + canfd_update_irq(s); +} + +static void canfd_config_mode(XlnxVersalCANFDState *s) +{ + register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]); + register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]); + register_reset(&s->reg_info[R_STATUS_REGISTER]); + + /* Put XlnxVersalCANFDState in configuration mode. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR_BIT, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW_1, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0); + + /* Clear the time stamp. */ + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); + + canfd_update_irq(s); +} + +static void update_status_register_mode_bits(XlnxVersalCANFDState *s) +{ + bool sleep_status = ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP); + bool sleep_mode = ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP); + /* Wake up interrupt bit. */ + bool wakeup_irq_val = !sleep_mode && sleep_status; + /* Sleep interrupt bit. */ + bool sleep_irq_val = sleep_mode && !sleep_status; + + /* Clear previous core mode status bits. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0); + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0); + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0); + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0); + + /* set current mode bit and generate irqs accordingly. */ + if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) { + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1); + } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) { + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, + sleep_irq_val); + } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1); + } else { + /* If all bits are zero, XlnxVersalCANFDState is set in normal mode. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1); + /* Set wakeup interrupt bit. */ + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, + wakeup_irq_val); + } + + /* Put the CANFD in error active state. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ESTAT, 1); + + canfd_update_irq(s); +} + +static uint64_t canfd_msr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + uint8_t multi_mode = 0; + + /* + * Multiple mode set check. This is done to make sure user doesn't set + * multiple modes. + */ + multi_mode = FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) + + FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) + + FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP); + + if (multi_mode > 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Attempting to configure several modes" + " simultaneously. One mode will be selected according to" + " their priority: LBACK > SLEEP > SNOOP.\n"); + } + + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + /* In configuration mode, any mode can be selected. */ + s->regs[R_MODE_SELECT_REGISTER] = val; + } else { + bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP); + + ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit); + + if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) { + qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set LBACK mode" + " without setting CEN bit as 0\n"); + } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) { + qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set SNOOP mode" + " without setting CEN bit as 0\n"); + } + + update_status_register_mode_bits(s); + } + + return s->regs[R_MODE_SELECT_REGISTER]; +} + +static void canfd_exit_sleep_mode(XlnxVersalCANFDState *s) +{ + ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0); + update_status_register_mode_bits(s); +} + +static void regs2frame(XlnxVersalCANFDState *s, qemu_can_frame *frame, + uint32_t reg_num) +{ + uint32_t i = 0; + uint32_t j = 0; + uint32_t val = 0; + uint32_t dlc_reg_val = 0; + uint32_t dlc_value = 0; + + /* Check that reg_num should be within TX register space. */ + assert(reg_num <= R_TB_ID_REGISTER + (NUM_REGS_PER_MSG_SPACE * + s->cfg.tx_fifo)); + + dlc_reg_val = s->regs[reg_num + 1]; + dlc_value = FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, DLC); + + frame->can_id = s->regs[reg_num]; + + if (FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, FDF)) { + /* + * CANFD frame. + * Converting dlc(0 to 15) 4 Byte data to plain length(i.e. 0 to 64) + * 1 Byte data. This is done to make it work with SocketCAN. + * On actual CANFD frame, this value can't be more than 0xF. + * Conversion table for DLC to plain length: + * + * DLC Plain Length + * 0 - 8 0 - 8 + * 9 9 - 12 + * 10 13 - 16 + * 11 17 - 20 + * 12 21 - 24 + * 13 25 - 32 + * 14 33 - 48 + * 15 49 - 64 + */ + + frame->flags = QEMU_CAN_FRMF_TYPE_FD; + + if (dlc_value < 8) { + frame->can_dlc = dlc_value; + } else { + assert((dlc_value - 8) < ARRAY_SIZE(canfd_dlc_array)); + frame->can_dlc = canfd_dlc_array[dlc_value - 8]; + } + } else { + /* + * FD Format bit not set that means it is a CAN Frame. + * Conversion table for classic CAN: + * + * DLC Plain Length + * 0 - 7 0 - 7 + * 8 - 15 8 + */ + + if (dlc_value > 8) { + frame->can_dlc = 8; + qemu_log_mask(LOG_GUEST_ERROR, "Maximum DLC value for Classic CAN" + " frame is 8. Only 8 byte data will be sent.\n"); + } else { + frame->can_dlc = dlc_value; + } + } + + for (j = 0; j < frame->can_dlc; j++) { + val = 8 * i; + + frame->data[j] = extract32(s->regs[reg_num + 2 + (j / 4)], val, 8); + i++; + + if (i % 4 == 0) { + i = 0; + } + } +} + +static void process_cancellation_requests(XlnxVersalCANFDState *s) +{ + uint32_t clear_mask = s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER] & + s->regs[R_TX_BUFFER_CANCEL_REQUEST_REGISTER]; + + s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER] &= ~clear_mask; + s->regs[R_TX_BUFFER_CANCEL_REQUEST_REGISTER] &= ~clear_mask; + + canfd_update_irq(s); +} + +static void store_rx_sequential(XlnxVersalCANFDState *s, + const qemu_can_frame *frame, + uint32_t fill_level, uint32_t read_index, + uint32_t store_location, uint8_t rx_fifo, + bool rx_fifo_id, uint8_t filter_index) +{ + int i; + bool is_canfd_frame; + uint8_t dlc = frame->can_dlc; + uint8_t rx_reg_num = 0; + uint32_t dlc_reg_val = 0; + uint32_t data_reg_val = 0; + + /* Getting RX0/1 fill level */ + if ((fill_level) > rx_fifo - 1) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: RX%d Buffer is full. Discarding the" + " message\n", path, rx_fifo_id); + + /* Set the corresponding RF buffer overflow interrupt. */ + if (rx_fifo_id == 0) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW, 1); + } else { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW_1, 1); + } + } else { + uint16_t rx_timestamp = CANFD_TIMER_MAX - + ptimer_get_count(s->canfd_timer); + + if (rx_timestamp == 0xFFFF) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TSCNT_OFLW, 1); + } else { + ARRAY_FIELD_DP32(s->regs, TIMESTAMP_REGISTER, TIMESTAMP_CNT, + rx_timestamp); + } + + if (rx_fifo_id == 0) { + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL, + fill_level + 1); + assert(store_location <= + R_RB_ID_REGISTER + (s->cfg.rx0_fifo * + NUM_REGS_PER_MSG_SPACE)); + } else { + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL_1, + fill_level + 1); + assert(store_location <= + R_RB_ID_REGISTER_1 + (s->cfg.rx1_fifo * + NUM_REGS_PER_MSG_SPACE)); + } + + s->regs[store_location] = frame->can_id; + + dlc = frame->can_dlc; + + if (frame->flags == QEMU_CAN_FRMF_TYPE_FD) { + is_canfd_frame = true; + + /* Store dlc value in Xilinx specific format. */ + for (i = 0; i < ARRAY_SIZE(canfd_dlc_array); i++) { + if (canfd_dlc_array[i] == frame->can_dlc) { + dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, 8 + i); + } + } + } else { + is_canfd_frame = false; + + if (frame->can_dlc > 8) { + dlc = 8; + } + + dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, dlc); + } + + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, FDF, is_canfd_frame); + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, TIMESTAMP, rx_timestamp); + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, MATCHED_FILTER_INDEX, + filter_index); + s->regs[store_location + 1] = dlc_reg_val; + + for (i = 0; i < dlc; i++) { + /* Register size is 4 byte but frame->data each is 1 byte. */ + switch (i % 4) { + case 0: + rx_reg_num = i / 4; + + data_reg_val = FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES3, + frame->data[i]); + break; + case 1: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES2, + frame->data[i]); + break; + case 2: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES1, + frame->data[i]); + break; + case 3: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES0, + frame->data[i]); + /* + * Last Bytes data which means we have all 4 bytes ready to + * store in one rx regs. + */ + s->regs[store_location + rx_reg_num + 2] = data_reg_val; + break; + } + } + + if (i % 4) { + /* + * In case DLC is not multiplier of 4, data is not saved to RX FIFO + * in above switch case. Store the remaining bytes here. + */ + s->regs[store_location + rx_reg_num + 2] = data_reg_val; + } + + /* set the interrupt as RXOK. */ + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); + } +} + +static void update_rx_sequential(XlnxVersalCANFDState *s, + const qemu_can_frame *frame) +{ + bool filter_pass = false; + uint8_t filter_index = 0; + int i; + int filter_partition = ARRAY_FIELD_EX32(s->regs, + RX_FIFO_WATERMARK_REGISTER, RXFP); + uint32_t store_location; + uint32_t fill_level; + uint32_t read_index; + uint8_t store_index = 0; + g_autofree char *path = NULL; + /* + * If all UAF bits are set to 0, then received messages are not stored + * in the RX buffers. + */ + if (s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER]) { + uint32_t acceptance_filter_status = + s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER]; + + for (i = 0; i < 32; i++) { + if (acceptance_filter_status & 0x1) { + uint32_t msg_id_masked = s->regs[R_AFMR_REGISTER + 2 * i] & + frame->can_id; + uint32_t afir_id_masked = s->regs[R_AFIR_REGISTER + 2 * i] & + s->regs[R_AFMR_REGISTER + 2 * i]; + uint16_t std_msg_id_masked = FIELD_EX32(msg_id_masked, + AFIR_REGISTER, AIID); + uint16_t std_afir_id_masked = FIELD_EX32(afir_id_masked, + AFIR_REGISTER, AIID); + uint32_t ext_msg_id_masked = FIELD_EX32(msg_id_masked, + AFIR_REGISTER, + AIID_EXT); + uint32_t ext_afir_id_masked = FIELD_EX32(afir_id_masked, + AFIR_REGISTER, + AIID_EXT); + bool ext_ide = FIELD_EX32(s->regs[R_AFMR_REGISTER + 2 * i], + AFMR_REGISTER, AMIDE); + + if (std_msg_id_masked == std_afir_id_masked) { + if (ext_ide) { + /* Extended message ID message. */ + if (ext_msg_id_masked == ext_afir_id_masked) { + filter_pass = true; + filter_index = i; + + break; + } + } else { + /* Standard message ID. */ + filter_pass = true; + filter_index = i; + + break; + } + } + } + acceptance_filter_status >>= 1; + } + } + + if (!filter_pass) { + path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_rx_fifo_filter_reject(path, frame->can_id, + frame->can_dlc); + } else { + if (filter_index <= filter_partition) { + fill_level = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, FL); + read_index = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, RI); + store_index = read_index + fill_level; + + if (read_index == s->cfg.rx0_fifo - 1) { + /* + * When ri is s->cfg.rx0_fifo - 1 i.e. max, it goes cyclic that + * means we reset the ri to 0x0. + */ + read_index = 0; + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI, + read_index); + } + + if (store_index > s->cfg.rx0_fifo - 1) { + store_index -= s->cfg.rx0_fifo - 1; + } + + store_location = R_RB_ID_REGISTER + + (store_index * NUM_REGS_PER_MSG_SPACE); + + store_rx_sequential(s, frame, fill_level, read_index, + store_location, s->cfg.rx0_fifo, 0, + filter_index); + } else { + /* RX 1 fill level message */ + fill_level = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, + FL_1); + read_index = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, + RI_1); + store_index = read_index + fill_level; + + if (read_index == s->cfg.rx1_fifo - 1) { + /* + * When ri is s->cfg.rx1_fifo - 1 i.e. max, it goes cyclic that + * means we reset the ri to 0x0. + */ + read_index = 0; + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI_1, + read_index); + } + + if (store_index > s->cfg.rx1_fifo - 1) { + store_index -= s->cfg.rx1_fifo - 1; + } + + store_location = R_RB_ID_REGISTER_1 + + (store_index * NUM_REGS_PER_MSG_SPACE); + + store_rx_sequential(s, frame, fill_level, read_index, + store_location, s->cfg.rx1_fifo, 1, + filter_index); + } + + path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_rx_data(path, frame->can_id, frame->can_dlc, + frame->flags); + canfd_update_irq(s); + } +} + +static bool tx_ready_check(XlnxVersalCANFDState *s) +{ + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" + " XlnxVersalCANFDState is in reset mode\n", path); + + return false; + } + + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" + " XlnxVersalCANFDState is in configuration mode." + " Reset the core so operations can start fresh\n", + path); + return false; + } + + if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" + " XlnxVersalCANFDState is in SNOOP MODE\n", + path); + return false; + } + + return true; +} + +static void tx_fifo_stamp(XlnxVersalCANFDState *s, uint32_t tb0_regid) +{ + /* + * If EFC bit in DLC message is set, this means we will store the + * event of this transmitted message with time stamp. + */ + uint32_t dlc_reg_val = 0; + + if (FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, EFC)) { + uint8_t dlc_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + DLC); + bool fdf_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + FDF); + bool brs_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + BRS); + uint8_t mm_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + MM); + uint8_t fill_level = ARRAY_FIELD_EX32(s->regs, + TX_EVENT_FIFO_STATUS_REGISTER, + TXE_FL); + uint8_t read_index = ARRAY_FIELD_EX32(s->regs, + TX_EVENT_FIFO_STATUS_REGISTER, + TXE_RI); + uint8_t store_index = fill_level + read_index; + + if ((fill_level) > s->cfg.tx_fifo - 1) { + qemu_log_mask(LOG_GUEST_ERROR, "TX Event Buffer is full." + " Discarding the message\n"); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXEOFLW, 1); + } else { + if (read_index == s->cfg.tx_fifo - 1) { + /* + * When ri is s->cfg.tx_fifo - 1 i.e. max, it goes cyclic that + * means we reset the ri to 0x0. + */ + read_index = 0; + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, + read_index); + } + + if (store_index > s->cfg.tx_fifo - 1) { + store_index -= s->cfg.tx_fifo - 1; + } + + assert(store_index < s->cfg.tx_fifo); + + uint32_t tx_event_reg0_id = R_TXE_FIFO_TB_ID_REGISTER + + (store_index * 2); + + /* Store message ID in TX event register. */ + s->regs[tx_event_reg0_id] = s->regs[tb0_regid]; + + uint16_t tx_timestamp = CANFD_TIMER_MAX - + ptimer_get_count(s->canfd_timer); + + /* Store DLC with time stamp in DLC regs. */ + dlc_reg_val = FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, DLC, dlc_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, FDF, + fdf_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, BRS, + brs_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, ET, 0x3); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, MM, mm_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, TIMESTAMP, + tx_timestamp); + s->regs[tx_event_reg0_id + 1] = dlc_reg_val; + + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL, + fill_level + 1); + } + } +} + +static gint g_cmp_ids(gconstpointer data1, gconstpointer data2) +{ + tx_ready_reg_info *tx_reg_1 = (tx_ready_reg_info *) data1; + tx_ready_reg_info *tx_reg_2 = (tx_ready_reg_info *) data2; + + return tx_reg_1->can_id - tx_reg_2->can_id; +} + +static void free_list(GSList *list) +{ + GSList *iterator = NULL; + + for (iterator = list; iterator != NULL; iterator = iterator->next) { + g_free((tx_ready_reg_info *)iterator->data); + } + + g_slist_free(list); + + return; +} + +static GSList *prepare_tx_data(XlnxVersalCANFDState *s) +{ + uint8_t i = 0; + GSList *list = NULL; + uint32_t reg_num = 0; + uint32_t reg_ready = s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER]; + + /* First find the messages which are ready for transmission. */ + for (i = 0; i < s->cfg.tx_fifo; i++) { + if (reg_ready & 1) { + reg_num = R_TB_ID_REGISTER + (NUM_REGS_PER_MSG_SPACE * i); + tx_ready_reg_info *temp = g_new(tx_ready_reg_info, 1); + + temp->can_id = s->regs[reg_num]; + temp->reg_num = reg_num; + list = g_slist_prepend(list, temp); + list = g_slist_sort(list, g_cmp_ids); + } + + reg_ready >>= 1; + } + + s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER] = 0; + s->regs[R_TX_BUFFER_CANCEL_REQUEST_REGISTER] = 0; + + return list; +} + +static void transfer_data(XlnxVersalCANFDState *s) +{ + bool canfd_tx = tx_ready_check(s); + GSList *list, *iterator = NULL; + qemu_can_frame frame; + + if (!canfd_tx) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller not enabled for data" + " transfer\n", path); + return; + } + + list = prepare_tx_data(s); + if (list == NULL) { + return; + } + + for (iterator = list; iterator != NULL; iterator = iterator->next) { + regs2frame(s, &frame, + ((tx_ready_reg_info *)iterator->data)->reg_num); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { + update_rx_sequential(s, &frame); + tx_fifo_stamp(s, ((tx_ready_reg_info *)iterator->data)->reg_num); + + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_tx_data(path, frame.can_id, frame.can_dlc, + frame.flags); + can_bus_client_send(&s->bus_client, &frame, 1); + tx_fifo_stamp(s, + ((tx_ready_reg_info *)iterator->data)->reg_num); + + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXRRS, 1); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) { + canfd_exit_sleep_mode(s); + } + } + } + + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1); + free_list(list); + + canfd_update_irq(s); +} + +static uint64_t canfd_srr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN, + FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN)); + + if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_reset(path, val64); + + /* First, core will do software reset then will enter in config mode. */ + canfd_config_reset(s); + } else if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + canfd_config_mode(s); + } else { + /* + * Leave config mode. Now XlnxVersalCANFD core will enter Normal, Sleep, + * snoop or Loopback mode depending upon LBACK, SLEEP, SNOOP register + * states. + */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0); + + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); + update_status_register_mode_bits(s); + transfer_data(s); + } + + return s->regs[R_SOFTWARE_RESET_REGISTER]; +} + +static uint64_t filter_mask(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t reg_idx = (reg->access->addr) / 4; + uint32_t val = val64; + uint32_t filter_offset = (reg_idx - R_AFMR_REGISTER) / 2; + + if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & + (1 << filter_offset))) { + s->regs[reg_idx] = val; + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d not enabled\n", + path, filter_offset + 1); + } + + return s->regs[reg_idx]; +} + +static uint64_t filter_id(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + hwaddr reg_idx = (reg->access->addr) / 4; + uint32_t val = val64; + uint32_t filter_offset = (reg_idx - R_AFIR_REGISTER) / 2; + + if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & + (1 << filter_offset))) { + s->regs[reg_idx] = val; + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d not enabled\n", + path, filter_offset + 1); + } + + return s->regs[reg_idx]; +} + +static uint64_t canfd_tx_fifo_status_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + uint8_t read_ind = 0; + uint8_t fill_ind = ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, + TXE_FL); + + if (FIELD_EX32(val, TX_EVENT_FIFO_STATUS_REGISTER, TXE_IRI) && fill_ind) { + read_ind = ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, + TXE_RI) + 1; + + if (read_ind > s->cfg.tx_fifo - 1) { + read_ind = 0; + } + + /* + * Increase the read index by 1 and decrease the fill level by 1. + */ + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, + read_ind); + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL, + fill_ind - 1); + } + + return s->regs[R_TX_EVENT_FIFO_STATUS_REGISTER]; +} + +static uint64_t canfd_rx_fifo_status_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + uint8_t read_ind = 0; + uint8_t fill_ind = 0; + + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, IRI)) { + /* FL index is zero, setting IRI bit has no effect. */ + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL) != 0) { + read_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, RI) + 1; + + if (read_ind > s->cfg.rx0_fifo - 1) { + read_ind = 0; + } + + fill_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL) - 1; + + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI, read_ind); + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL, fill_ind); + } + } + + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, IRI_1)) { + /* FL_1 index is zero, setting IRI_1 bit has no effect. */ + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL_1) != 0) { + read_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, RI_1) + 1; + + if (read_ind > s->cfg.rx1_fifo - 1) { + read_ind = 0; + } + + fill_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL_1) - 1; + + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI_1, read_ind); + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL_1, fill_ind); + } + } + + return s->regs[R_RX_FIFO_STATUS_REGISTER]; +} + +static uint64_t canfd_tsr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) { + ARRAY_FIELD_DP32(s->regs, TIMESTAMP_REGISTER, TIMESTAMP_CNT, 0); + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); + } + + return 0; +} + +static uint64_t canfd_trr_reg_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in SNOOP mode." + " tx_ready_register will stay in reset mode\n", path); + return 0; + } else { + return val64; + } +} + +static void canfd_trr_reg_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + transfer_data(s); +} + +static void canfd_cancel_reg_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + process_cancellation_requests(s); +} + +static uint64_t canfd_write_check_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + return val; + } + return 0; +} + +static const RegisterAccessInfo canfd_tx_regs[] = { + { .name = "TB_ID_REGISTER", .addr = A_TB_ID_REGISTER, + },{ .name = "TB0_DLC_REGISTER", .addr = A_TB0_DLC_REGISTER, + },{ .name = "TB_DW0_REGISTER", .addr = A_TB_DW0_REGISTER, + },{ .name = "TB_DW1_REGISTER", .addr = A_TB_DW1_REGISTER, + },{ .name = "TB_DW2_REGISTER", .addr = A_TB_DW2_REGISTER, + },{ .name = "TB_DW3_REGISTER", .addr = A_TB_DW3_REGISTER, + },{ .name = "TB_DW4_REGISTER", .addr = A_TB_DW4_REGISTER, + },{ .name = "TB_DW5_REGISTER", .addr = A_TB_DW5_REGISTER, + },{ .name = "TB_DW6_REGISTER", .addr = A_TB_DW6_REGISTER, + },{ .name = "TB_DW7_REGISTER", .addr = A_TB_DW7_REGISTER, + },{ .name = "TB_DW8_REGISTER", .addr = A_TB_DW8_REGISTER, + },{ .name = "TB_DW9_REGISTER", .addr = A_TB_DW9_REGISTER, + },{ .name = "TB_DW10_REGISTER", .addr = A_TB_DW10_REGISTER, + },{ .name = "TB_DW11_REGISTER", .addr = A_TB_DW11_REGISTER, + },{ .name = "TB_DW12_REGISTER", .addr = A_TB_DW12_REGISTER, + },{ .name = "TB_DW13_REGISTER", .addr = A_TB_DW13_REGISTER, + },{ .name = "TB_DW14_REGISTER", .addr = A_TB_DW14_REGISTER, + },{ .name = "TB_DW15_REGISTER", .addr = A_TB_DW15_REGISTER, + } +}; + +static const RegisterAccessInfo canfd_rx0_regs[] = { + { .name = "RB_ID_REGISTER", .addr = A_RB_ID_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DLC_REGISTER", .addr = A_RB_DLC_REGISTER, + .ro = 0xfe1fffff, + },{ .name = "RB_DW0_REGISTER", .addr = A_RB_DW0_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW1_REGISTER", .addr = A_RB_DW1_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW2_REGISTER", .addr = A_RB_DW2_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW3_REGISTER", .addr = A_RB_DW3_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW4_REGISTER", .addr = A_RB_DW4_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW5_REGISTER", .addr = A_RB_DW5_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW6_REGISTER", .addr = A_RB_DW6_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW7_REGISTER", .addr = A_RB_DW7_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW8_REGISTER", .addr = A_RB_DW8_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW9_REGISTER", .addr = A_RB_DW9_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW10_REGISTER", .addr = A_RB_DW10_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW11_REGISTER", .addr = A_RB_DW11_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW12_REGISTER", .addr = A_RB_DW12_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW13_REGISTER", .addr = A_RB_DW13_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW14_REGISTER", .addr = A_RB_DW14_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW15_REGISTER", .addr = A_RB_DW15_REGISTER, + .ro = 0xffffffff, + } +}; + +static const RegisterAccessInfo canfd_rx1_regs[] = { + { .name = "RB_ID_REGISTER_1", .addr = A_RB_ID_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DLC_REGISTER_1", .addr = A_RB_DLC_REGISTER_1, + .ro = 0xfe1fffff, + },{ .name = "RB0_DW0_REGISTER_1", .addr = A_RB0_DW0_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW1_REGISTER_1", .addr = A_RB_DW1_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW2_REGISTER_1", .addr = A_RB_DW2_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW3_REGISTER_1", .addr = A_RB_DW3_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW4_REGISTER_1", .addr = A_RB_DW4_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW5_REGISTER_1", .addr = A_RB_DW5_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW6_REGISTER_1", .addr = A_RB_DW6_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW7_REGISTER_1", .addr = A_RB_DW7_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW8_REGISTER_1", .addr = A_RB_DW8_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW9_REGISTER_1", .addr = A_RB_DW9_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW10_REGISTER_1", .addr = A_RB_DW10_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW11_REGISTER_1", .addr = A_RB_DW11_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW12_REGISTER_1", .addr = A_RB_DW12_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW13_REGISTER_1", .addr = A_RB_DW13_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW14_REGISTER_1", .addr = A_RB_DW14_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW15_REGISTER_1", .addr = A_RB_DW15_REGISTER_1, + .ro = 0xffffffff, + } +}; + +/* Acceptance filter registers. */ +static const RegisterAccessInfo canfd_af_regs[] = { + { .name = "AFMR_REGISTER", .addr = A_AFMR_REGISTER, + .pre_write = filter_mask, + },{ .name = "AFIR_REGISTER", .addr = A_AFIR_REGISTER, + .pre_write = filter_id, + } +}; + +static const RegisterAccessInfo canfd_txe_regs[] = { + { .name = "TXE_FIFO_TB_ID_REGISTER", .addr = A_TXE_FIFO_TB_ID_REGISTER, + .ro = 0xffffffff, + },{ .name = "TXE_FIFO_TB_DLC_REGISTER", .addr = A_TXE_FIFO_TB_DLC_REGISTER, + .ro = 0xffffffff, + } +}; + +static const RegisterAccessInfo canfd_regs_info[] = { + { .name = "SOFTWARE_RESET_REGISTER", .addr = A_SOFTWARE_RESET_REGISTER, + .pre_write = canfd_srr_pre_write, + },{ .name = "MODE_SELECT_REGISTER", .addr = A_MODE_SELECT_REGISTER, + .pre_write = canfd_msr_pre_write, + },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER", + .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER", + .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "ERROR_COUNTER_REGISTER", .addr = A_ERROR_COUNTER_REGISTER, + .ro = 0xffff, + },{ .name = "ERROR_STATUS_REGISTER", .addr = A_ERROR_STATUS_REGISTER, + .w1c = 0xf1f, + },{ .name = "STATUS_REGISTER", .addr = A_STATUS_REGISTER, + .reset = 0x1, + .ro = 0x7f17ff, + },{ .name = "INTERRUPT_STATUS_REGISTER", + .addr = A_INTERRUPT_STATUS_REGISTER, + .ro = 0xffffff7f, + },{ .name = "INTERRUPT_ENABLE_REGISTER", + .addr = A_INTERRUPT_ENABLE_REGISTER, + .post_write = canfd_ier_post_write, + },{ .name = "INTERRUPT_CLEAR_REGISTER", + .addr = A_INTERRUPT_CLEAR_REGISTER, .pre_write = canfd_icr_pre_write, + },{ .name = "TIMESTAMP_REGISTER", .addr = A_TIMESTAMP_REGISTER, + .ro = 0xffff0000, + .pre_write = canfd_tsr_pre_write, + },{ .name = "DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER", + .addr = A_DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "DATA_PHASE_BIT_TIMING_REGISTER", + .addr = A_DATA_PHASE_BIT_TIMING_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "TX_BUFFER_READY_REQUEST_REGISTER", + .addr = A_TX_BUFFER_READY_REQUEST_REGISTER, + .pre_write = canfd_trr_reg_prew, + .post_write = canfd_trr_reg_postw, + },{ .name = "INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER", + .addr = A_INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, + },{ .name = "TX_BUFFER_CANCEL_REQUEST_REGISTER", + .addr = A_TX_BUFFER_CANCEL_REQUEST_REGISTER, + .post_write = canfd_cancel_reg_postw, + },{ .name = "INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER", + .addr = A_INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, + },{ .name = "TX_EVENT_FIFO_STATUS_REGISTER", + .addr = A_TX_EVENT_FIFO_STATUS_REGISTER, + .ro = 0x3f1f, .pre_write = canfd_tx_fifo_status_prew, + },{ .name = "TX_EVENT_FIFO_WATERMARK_REGISTER", + .addr = A_TX_EVENT_FIFO_WATERMARK_REGISTER, + .reset = 0xf, + .pre_write = canfd_write_check_prew, + },{ .name = "ACCEPTANCE_FILTER_CONTROL_REGISTER", + .addr = A_ACCEPTANCE_FILTER_CONTROL_REGISTER, + },{ .name = "RX_FIFO_STATUS_REGISTER", .addr = A_RX_FIFO_STATUS_REGISTER, + .ro = 0x7f3f7f3f, .pre_write = canfd_rx_fifo_status_prew, + },{ .name = "RX_FIFO_WATERMARK_REGISTER", + .addr = A_RX_FIFO_WATERMARK_REGISTER, + .reset = 0x1f0f0f, + .pre_write = canfd_write_check_prew, + } +}; + +static void xlnx_versal_canfd_ptimer_cb(void *opaque) +{ + /* No action required on the timer rollover. */ +} + +static const MemoryRegionOps canfd_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void canfd_reset(DeviceState *dev) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->reg_info); ++i) { + register_reset(&s->reg_info[i]); + } + + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); +} + +static bool can_xilinx_canfd_receive(CanBusClientState *client) +{ + XlnxVersalCANFDState *s = container_of(client, XlnxVersalCANFDState, + bus_client); + + bool reset_state = ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST); + bool can_enabled = ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN); + + return !reset_state && can_enabled; +} + +static ssize_t canfd_xilinx_receive(CanBusClientState *client, + const qemu_can_frame *buf, + size_t buf_size) +{ + XlnxVersalCANFDState *s = container_of(client, XlnxVersalCANFDState, + bus_client); + const qemu_can_frame *frame = buf; + + assert(buf_size > 0); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { + /* + * XlnxVersalCANFDState will not participate in normal bus communication + * and does not receive any messages transmitted by other CAN nodes. + */ + return 1; + } + + /* Update the status register that we are receiving message. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, BBSY, 1); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) { + /* Snoop Mode: Just keep the data. no response back. */ + update_rx_sequential(s, frame); + } else { + if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) { + /* + * XlnxVersalCANFDState is in sleep mode. Any data on bus will bring + * it to the wake up state. + */ + canfd_exit_sleep_mode(s); + } + + update_rx_sequential(s, frame); + } + + /* Message processing done. Update the status back to !busy */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, BBSY, 0); + return 1; +} + +static CanBusClientInfo canfd_xilinx_bus_client_info = { + .can_receive = can_xilinx_canfd_receive, + .receive = canfd_xilinx_receive, +}; + +static int xlnx_canfd_connect_to_bus(XlnxVersalCANFDState *s, + CanBusState *bus) +{ + s->bus_client.info = &canfd_xilinx_bus_client_info; + + return can_bus_insert_client(bus, &s->bus_client); +} + +#define NUM_REG_PER_AF ARRAY_SIZE(canfd_af_regs) +#define NUM_AF 32 +#define NUM_REG_PER_TXE ARRAY_SIZE(canfd_txe_regs) +#define NUM_TXE 32 + +static int canfd_populate_regarray(XlnxVersalCANFDState *s, + RegisterInfoArray *r_array, int pos, + const RegisterAccessInfo *rae, + int num_rae) +{ + int i; + + for (i = 0; i < num_rae; i++) { + int index = rae[i].addr / 4; + RegisterInfo *r = &s->reg_info[index]; + + object_initialize(r, sizeof(*r), TYPE_REGISTER); + + *r = (RegisterInfo) { + .data = &s->regs[index], + .data_size = sizeof(uint32_t), + .access = &rae[i], + .opaque = OBJECT(s), + }; + + r_array->r[i + pos] = r; + } + return i + pos; +} + +static void canfd_create_rai(RegisterAccessInfo *rai_array, + const RegisterAccessInfo *canfd_regs, + int template_rai_array_sz, + int num_template_to_copy) +{ + int i; + int reg_num; + + for (reg_num = 0; reg_num < num_template_to_copy; reg_num++) { + int pos = reg_num * template_rai_array_sz; + + memcpy(rai_array + pos, canfd_regs, + template_rai_array_sz * sizeof(RegisterAccessInfo)); + + for (i = 0; i < template_rai_array_sz; i++) { + const char *name = canfd_regs[i].name; + uint64_t addr = canfd_regs[i].addr; + rai_array[i + pos].name = g_strdup_printf("%s%d", name, reg_num); + rai_array[i + pos].addr = addr + pos * 4; + } + } +} + +static RegisterInfoArray *canfd_create_regarray(XlnxVersalCANFDState *s) +{ + const char *device_prefix = object_get_typename(OBJECT(s)); + uint64_t memory_size = XLNX_VERSAL_CANFD_R_MAX * 4; + int num_regs; + int pos = 0; + RegisterInfoArray *r_array; + + num_regs = ARRAY_SIZE(canfd_regs_info) + + s->cfg.tx_fifo * NUM_REGS_PER_MSG_SPACE + + s->cfg.rx0_fifo * NUM_REGS_PER_MSG_SPACE + + NUM_AF * NUM_REG_PER_AF + + NUM_TXE * NUM_REG_PER_TXE; + + s->tx_regs = g_new0(RegisterAccessInfo, + s->cfg.tx_fifo * ARRAY_SIZE(canfd_tx_regs)); + + canfd_create_rai(s->tx_regs, canfd_tx_regs, + ARRAY_SIZE(canfd_tx_regs), s->cfg.tx_fifo); + + s->rx0_regs = g_new0(RegisterAccessInfo, + s->cfg.rx0_fifo * ARRAY_SIZE(canfd_rx0_regs)); + + canfd_create_rai(s->rx0_regs, canfd_rx0_regs, + ARRAY_SIZE(canfd_rx0_regs), s->cfg.rx0_fifo); + + s->af_regs = g_new0(RegisterAccessInfo, + NUM_AF * ARRAY_SIZE(canfd_af_regs)); + + canfd_create_rai(s->af_regs, canfd_af_regs, + ARRAY_SIZE(canfd_af_regs), NUM_AF); + + s->txe_regs = g_new0(RegisterAccessInfo, + NUM_TXE * ARRAY_SIZE(canfd_txe_regs)); + + canfd_create_rai(s->txe_regs, canfd_txe_regs, + ARRAY_SIZE(canfd_txe_regs), NUM_TXE); + + if (s->cfg.enable_rx_fifo1) { + num_regs += s->cfg.rx1_fifo * NUM_REGS_PER_MSG_SPACE; + + s->rx1_regs = g_new0(RegisterAccessInfo, + s->cfg.rx1_fifo * ARRAY_SIZE(canfd_rx1_regs)); + + canfd_create_rai(s->rx1_regs, canfd_rx1_regs, + ARRAY_SIZE(canfd_rx1_regs), s->cfg.rx1_fifo); + } + + r_array = g_new0(RegisterInfoArray, 1); + r_array->r = g_new0(RegisterInfo * , num_regs); + r_array->num_elements = num_regs; + r_array->prefix = device_prefix; + + pos = canfd_populate_regarray(s, r_array, pos, + canfd_regs_info, + ARRAY_SIZE(canfd_regs_info)); + pos = canfd_populate_regarray(s, r_array, pos, + s->tx_regs, s->cfg.tx_fifo * + NUM_REGS_PER_MSG_SPACE); + pos = canfd_populate_regarray(s, r_array, pos, + s->rx0_regs, s->cfg.rx0_fifo * + NUM_REGS_PER_MSG_SPACE); + if (s->cfg.enable_rx_fifo1) { + pos = canfd_populate_regarray(s, r_array, pos, + s->rx1_regs, s->cfg.rx1_fifo * + NUM_REGS_PER_MSG_SPACE); + } + pos = canfd_populate_regarray(s, r_array, pos, + s->af_regs, NUM_AF * NUM_REG_PER_AF); + pos = canfd_populate_regarray(s, r_array, pos, + s->txe_regs, NUM_TXE * NUM_REG_PER_TXE); + + memory_region_init_io(&r_array->mem, OBJECT(s), &canfd_ops, r_array, + device_prefix, memory_size); + return r_array; +} + +static void canfd_realize(DeviceState *dev, Error **errp) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(dev); + RegisterInfoArray *reg_array; + + reg_array = canfd_create_regarray(s); + memory_region_add_subregion(&s->iomem, 0x00, ®_array->mem); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_canfd_int); + + if (s->canfdbus) { + if (xlnx_canfd_connect_to_bus(s, s->canfdbus) < 0) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + error_setg(errp, "%s: xlnx_canfd_connect_to_bus failed", path); + return; + } + + } + + /* Allocate a new timer. */ + s->canfd_timer = ptimer_init(xlnx_versal_canfd_ptimer_cb, s, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + + ptimer_transaction_begin(s->canfd_timer); + + ptimer_set_freq(s->canfd_timer, s->cfg.ext_clk_freq); + ptimer_set_limit(s->canfd_timer, CANFD_TIMER_MAX, 1); + ptimer_run(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); +} + +static void canfd_init(Object *obj) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(obj); + + memory_region_init(&s->iomem, obj, TYPE_XILINX_CANFD, + XLNX_VERSAL_CANFD_R_MAX * 4); +} + +static const VMStateDescription vmstate_canfd = { + .name = TYPE_XILINX_CANFD, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxVersalCANFDState, + XLNX_VERSAL_CANFD_R_MAX), + VMSTATE_PTIMER(canfd_timer, XlnxVersalCANFDState), + VMSTATE_END_OF_LIST(), + } +}; + +static Property canfd_core_properties[] = { + DEFINE_PROP_UINT8("rx-fifo0", XlnxVersalCANFDState, cfg.rx0_fifo, 0x40), + DEFINE_PROP_UINT8("rx-fifo1", XlnxVersalCANFDState, cfg.rx1_fifo, 0x40), + DEFINE_PROP_UINT8("tx-fifo", XlnxVersalCANFDState, cfg.tx_fifo, 0x20), + DEFINE_PROP_BOOL("enable-rx-fifo1", XlnxVersalCANFDState, + cfg.enable_rx_fifo1, true), + DEFINE_PROP_UINT32("ext_clk_freq", XlnxVersalCANFDState, cfg.ext_clk_freq, + CANFD_DEFAULT_CLOCK), + DEFINE_PROP_LINK("canfdbus", XlnxVersalCANFDState, canfdbus, TYPE_CAN_BUS, + CanBusState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void canfd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = canfd_reset; + dc->realize = canfd_realize; + device_class_set_props(dc, canfd_core_properties); + dc->vmsd = &vmstate_canfd; +} + +static const TypeInfo canfd_info = { + .name = TYPE_XILINX_CANFD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCANFDState), + .class_init = canfd_class_init, + .instance_init = canfd_init, +}; + +static void canfd_register_types(void) +{ + type_register_static(&canfd_info); +} + +type_init(canfd_register_types) diff --git a/include/hw/net/xlnx-versal-canfd.h b/include/hw/net/xlnx-versal-canfd.h new file mode 100644 index 0000000000..ad3104dd13 --- /dev/null +++ b/include/hw/net/xlnx-versal-canfd.h @@ -0,0 +1,87 @@ +/* + * QEMU model of the Xilinx Versal CANFD Controller. + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written-by: Vikram Garhwal + * Based on QEMU CANFD Device emulation implemented by Jin Yang, Deniz Eren and + * Pavel Pisa. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_CANFD_XILINX_H +#define HW_CANFD_XILINX_H + +#include "hw/register.h" +#include "hw/ptimer.h" +#include "net/can_emu.h" +#include "hw/qdev-clock.h" + +#define TYPE_XILINX_CANFD "xlnx.versal-canfd" + +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCANFDState, XILINX_CANFD) + +#define NUM_REGS_PER_MSG_SPACE 18 /* 1 ID + 1 DLC + 16 Data(DW0 - DW15) regs. */ +#define MAX_NUM_RX 64 +#define OFFSET_RX1_DW15 (0x4144 / 4) +#define CANFD_TIMER_MAX 0xFFFFUL +#define CANFD_DEFAULT_CLOCK (25 * 1000 * 1000) + +#define XLNX_VERSAL_CANFD_R_MAX (OFFSET_RX1_DW15 + \ + ((MAX_NUM_RX - 1) * NUM_REGS_PER_MSG_SPACE) + 1) + +typedef struct XlnxVersalCANFDState { + SysBusDevice parent_obj; + MemoryRegion iomem; + + qemu_irq irq_canfd_int; + qemu_irq irq_addr_err; + + RegisterInfo reg_info[XLNX_VERSAL_CANFD_R_MAX]; + RegisterAccessInfo *tx_regs; + RegisterAccessInfo *rx0_regs; + RegisterAccessInfo *rx1_regs; + RegisterAccessInfo *af_regs; + RegisterAccessInfo *txe_regs; + RegisterAccessInfo *rx_mailbox_regs; + RegisterAccessInfo *af_mask_regs_mailbox; + + uint32_t regs[XLNX_VERSAL_CANFD_R_MAX]; + + ptimer_state *canfd_timer; + + CanBusClientState bus_client; + CanBusState *canfdbus; + + struct { + uint8_t rx0_fifo; + uint8_t rx1_fifo; + uint8_t tx_fifo; + bool enable_rx_fifo1; + uint32_t ext_clk_freq; + } cfg; + +} XlnxVersalCANFDState; + +typedef struct tx_ready_reg_info { + uint32_t can_id; + uint32_t reg_num; +} tx_ready_reg_info; + +#endif From 042d6b02551eb40255a76131b2d60f996a51195c Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Tue, 6 Jun 2023 10:19:30 +0100 Subject: [PATCH 137/412] xlnx-versal: Connect Xilinx VERSAL CANFD controllers Connect CANFD0 and CANFD1 on the Versal-virt machine and update xlnx-versal-virt document with CANFD command line examples. Signed-off-by: Vikram Garhwal Reviewed-by: Peter Maydell Reviewed-by: Francisco Iglesias Signed-off-by: Peter Maydell --- docs/system/arm/xlnx-versal-virt.rst | 31 ++++++++++++++++ hw/arm/xlnx-versal-virt.c | 53 ++++++++++++++++++++++++++++ hw/arm/xlnx-versal.c | 37 +++++++++++++++++++ include/hw/arm/xlnx-versal.h | 12 +++++++ 4 files changed, 133 insertions(+) diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 92ad10d2da..d2d1b26692 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -34,6 +34,7 @@ Implemented devices: - DDR memory - BBRAM (36 bytes of Battery-backed RAM) - eFUSE (3072 bytes of one-time field-programmable bit array) +- 2 CANFDs QEMU does not yet model any other devices, including the PL and the AI Engine. @@ -224,3 +225,33 @@ To use a different index value, N, from default of 1, add: Better yet, do not use actual product data when running guest image on this Xilinx Versal Virt board. + +Using CANFDs for Versal Virt +"""""""""""""""""""""""""""" +Versal CANFD controller is developed based on SocketCAN and QEMU CAN bus +implementation. Bus connection and socketCAN connection for each CAN module +can be set through command lines. + +To connect both CANFD0 and CANFD1 on the same bus: + +.. code-block:: bash + + -object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus + +To connect CANFD0 and CANFD1 to separate buses: + +.. code-block:: bash + + -object can-bus,id=canbus0 -object can-bus,id=canbus1 \ + -machine canbus0=canbus0 -machine canbus1=canbus1 + +The SocketCAN interface can connect to a Physical or a Virtual CAN interfaces on +the host machine. Please check this document to learn about CAN interface on +Linux: docs/system/devices/can.rst + +To connect CANFD0 and CANFD1 to host machine's CAN interface can0: + +.. code-block:: bash + + -object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus + -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 668a9d65a4..1ee2b8697f 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -40,9 +40,11 @@ struct VersalVirt { uint32_t clk_25Mhz; uint32_t usb; uint32_t dwc; + uint32_t canfd[2]; } phandle; struct arm_boot_info binfo; + CanBusState *canbus[XLNX_VERSAL_NR_CANFD]; struct { bool secure; } cfg; @@ -235,6 +237,38 @@ static void fdt_add_uart_nodes(VersalVirt *s) } } +static void fdt_add_canfd_nodes(VersalVirt *s) +{ + uint64_t addrs[] = { MM_CANFD1, MM_CANFD0 }; + uint32_t size[] = { MM_CANFD1_SIZE, MM_CANFD0_SIZE }; + unsigned int irqs[] = { VERSAL_CANFD1_IRQ_0, VERSAL_CANFD0_IRQ_0 }; + const char clocknames[] = "can_clk\0s_axi_aclk"; + int i; + + /* Create and connect CANFD0 and CANFD1 nodes to canbus0. */ + for (i = 0; i < ARRAY_SIZE(addrs); i++) { + char *name = g_strdup_printf("/canfd@%" PRIx64, addrs[i]); + qemu_fdt_add_subnode(s->fdt, name); + + qemu_fdt_setprop_cell(s->fdt, name, "rx-fifo-depth", 0x40); + qemu_fdt_setprop_cell(s->fdt, name, "tx-mailbox-count", 0x20); + + qemu_fdt_setprop_cells(s->fdt, name, "clocks", + s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); + qemu_fdt_setprop(s->fdt, name, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irqs[i], + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, addrs[i], 2, size[i]); + qemu_fdt_setprop_string(s->fdt, name, "compatible", + "xlnx,canfd-2.0"); + + g_free(name); + } +} + static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname, uint32_t phandle) { @@ -639,12 +673,17 @@ static void versal_virt_init(MachineState *machine) TYPE_XLNX_VERSAL); object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram), &error_abort); + object_property_set_link(OBJECT(&s->soc), "canbus0", OBJECT(s->canbus[0]), + &error_abort); + object_property_set_link(OBJECT(&s->soc), "canbus1", OBJECT(s->canbus[1]), + &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); fdt_create(s); create_virtio_regions(s); fdt_add_gem_nodes(s); fdt_add_uart_nodes(s); + fdt_add_canfd_nodes(s); fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); fdt_add_zdma_nodes(s); @@ -712,6 +751,20 @@ static void versal_virt_init(MachineState *machine) static void versal_virt_machine_instance_init(Object *obj) { + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + /* + * User can set canbus0 and canbus1 properties to can-bus object and connect + * to socketcan(optional) interface via command line. + */ + object_property_add_link(obj, "canbus0", TYPE_CAN_BUS, + (Object **)&s->canbus[0], + object_property_allow_set_link, + 0); + object_property_add_link(obj, "canbus1", TYPE_CAN_BUS, + (Object **)&s->canbus[1], + object_property_allow_set_link, + 0); } static void versal_virt_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 69b1b99e93..1594dd6c5c 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -184,6 +184,38 @@ static void versal_create_uarts(Versal *s, qemu_irq *pic) } } +static void versal_create_canfds(Versal *s, qemu_irq *pic) +{ + int i; + uint32_t irqs[] = { VERSAL_CANFD0_IRQ_0, VERSAL_CANFD1_IRQ_0}; + uint64_t addrs[] = { MM_CANFD0, MM_CANFD1 }; + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.canfd); i++) { + char *name = g_strdup_printf("canfd%d", i); + SysBusDevice *sbd; + MemoryRegion *mr; + + object_initialize_child(OBJECT(s), name, &s->lpd.iou.canfd[i], + TYPE_XILINX_CANFD); + sbd = SYS_BUS_DEVICE(&s->lpd.iou.canfd[i]); + + object_property_set_int(OBJECT(&s->lpd.iou.canfd[i]), "ext_clk_freq", + XLNX_VERSAL_CANFD_REF_CLK , &error_abort); + + object_property_set_link(OBJECT(&s->lpd.iou.canfd[i]), "canfdbus", + OBJECT(s->lpd.iou.canbus[i]), + &error_abort); + + sysbus_realize(sbd, &error_fatal); + + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + + sysbus_connect_irq(sbd, 0, pic[irqs[i]]); + g_free(name); + } +} + static void versal_create_usbs(Versal *s, qemu_irq *pic) { DeviceState *dev; @@ -718,6 +750,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_gic(s, pic); versal_create_rpu_cpus(s); versal_create_uarts(s, pic); + versal_create_canfds(s, pic); versal_create_usbs(s, pic); versal_create_gems(s, pic); versal_create_admas(s, pic); @@ -757,6 +790,10 @@ static void versal_init(Object *obj) static Property versal_properties[] = { DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("canbus0", Versal, lpd.iou.canbus[0], + TYPE_CAN_BUS, CanBusState *), + DEFINE_PROP_LINK("canbus1", Versal, lpd.iou.canbus[1], + TYPE_CAN_BUS, CanBusState *), DEFINE_PROP_END_OF_LIST() }; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index b6786e9832..39ee31185c 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -31,6 +31,7 @@ #include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" +#include "hw/net/xlnx-versal-canfd.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) @@ -43,6 +44,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) #define XLNX_VERSAL_NR_SDS 2 #define XLNX_VERSAL_NR_XRAM 4 #define XLNX_VERSAL_NR_IRQS 192 +#define XLNX_VERSAL_NR_CANFD 2 +#define XLNX_VERSAL_CANFD_REF_CLK (24 * 1000 * 1000) struct Versal { /*< private >*/ @@ -73,6 +76,8 @@ struct Versal { CadenceGEMState gem[XLNX_VERSAL_NR_GEMS]; XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; VersalUsb2 usb; + CanBusState *canbus[XLNX_VERSAL_NR_CANFD]; + XlnxVersalCANFDState canfd[XLNX_VERSAL_NR_CANFD]; } iou; /* Real-time Processing Unit. */ @@ -133,6 +138,8 @@ struct Versal { #define VERSAL_CRL_IRQ 10 #define VERSAL_UART0_IRQ_0 18 #define VERSAL_UART1_IRQ_0 19 +#define VERSAL_CANFD0_IRQ_0 20 +#define VERSAL_CANFD1_IRQ_0 21 #define VERSAL_USB0_IRQ_0 22 #define VERSAL_GEM0_IRQ_0 56 #define VERSAL_GEM0_WAKE_IRQ_0 57 @@ -163,6 +170,11 @@ struct Versal { #define MM_UART1 0xff010000U #define MM_UART1_SIZE 0x10000 +#define MM_CANFD0 0xff060000U +#define MM_CANFD0_SIZE 0x10000 +#define MM_CANFD1 0xff070000U +#define MM_CANFD1_SIZE 0x10000 + #define MM_GEM0 0xff0c0000U #define MM_GEM0_SIZE 0x10000 #define MM_GEM1 0xff0d0000U From 1d2a60299c6fe08076a02c1df83173b31890e9b2 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Tue, 6 Jun 2023 10:19:31 +0100 Subject: [PATCH 138/412] MAINTAINERS: Include canfd tests under Xilinx CAN Signed-off-by: Vikram Garhwal Reviewed-by: Peter Maydell Reviewed-by: Francisco Iglesias Signed-off-by: Peter Maydell --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 55668d6336..4b2639def6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1819,7 +1819,7 @@ M: Francisco Iglesias S: Maintained F: hw/net/can/xlnx-* F: include/hw/net/xlnx-* -F: tests/qtest/xlnx-can-test* +F: tests/qtest/xlnx-can*-test* EDU M: Jiri Slaby From 8976fd2b5e13a2623e0ab36df64dd3ed14624023 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Tue, 6 Jun 2023 10:19:31 +0100 Subject: [PATCH 139/412] tests/qtest: Introduce tests for Xilinx VERSAL CANFD controller The QTests perform three tests on the Xilinx VERSAL CANFD controller: Tests the CANFD controllers in loopback. Tests the CANFD controllers in normal mode with CAN frame. Tests the CANFD controllers in normal mode with CANFD frame. Signed-off-by: Vikram Garhwal Acked-by: Thomas Huth Reviewed-by: Francisco Iglesias Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 1 + tests/qtest/xlnx-canfd-test.c | 423 ++++++++++++++++++++++++++++++++++ 2 files changed, 424 insertions(+) create mode 100644 tests/qtest/xlnx-canfd-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 087f2dc9d7..fd434069b7 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -215,6 +215,7 @@ qtests_aarch64 = \ (config_all.has_key('CONFIG_TCG') and config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? \ ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \ + (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test'] : []) + \ (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test'] : []) + \ (config_all.has_key('CONFIG_TCG') and \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ diff --git a/tests/qtest/xlnx-canfd-test.c b/tests/qtest/xlnx-canfd-test.c new file mode 100644 index 0000000000..76ee106d4f --- /dev/null +++ b/tests/qtest/xlnx-canfd-test.c @@ -0,0 +1,423 @@ +/* + * SPDX-License-Identifier: MIT + * + * QTests for the Xilinx Versal CANFD controller. + * + * Copyright (c) 2022 AMD Inc. + * + * Written-by: Vikram Garhwal + * + * 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 "libqtest.h" + +/* Base address. */ +#define CANFD0_BASE_ADDR 0xff060000 +#define CANFD1_BASE_ADDR 0xff070000 + +/* Register addresses. */ +#define R_SRR_OFFSET 0x00 +#define R_MSR_OFFSET 0x04 +#define R_FILTER_CONTROL_REGISTER 0xe0 +#define R_SR_OFFSET 0x18 +#define R_ISR_OFFSET 0x1c +#define R_IER_OFFSET 0x20 +#define R_ICR_OFFSET 0x24 +#define R_TX_READY_REQ_REGISTER 0x90 +#define RX_FIFO_STATUS_REGISTER 0xe8 +#define R_TXID_OFFSET 0x100 +#define R_TXDLC_OFFSET 0x104 +#define R_TXDATA1_OFFSET 0x108 +#define R_TXDATA2_OFFSET 0x10c +#define R_AFMR_REGISTER0 0xa00 +#define R_AFIR_REGISTER0 0xa04 +#define R_RX0_ID_OFFSET 0x2100 +#define R_RX0_DLC_OFFSET 0x2104 +#define R_RX0_DATA1_OFFSET 0x2108 +#define R_RX0_DATA2_OFFSET 0x210c + +/* CANFD modes. */ +#define SRR_CONFIG_MODE 0x00 +#define MSR_NORMAL_MODE 0x00 +#define MSR_LOOPBACK_MODE (1 << 1) +#define ENABLE_CANFD (1 << 1) + +/* CANFD status. */ +#define STATUS_CONFIG_MODE (1 << 0) +#define STATUS_NORMAL_MODE (1 << 3) +#define STATUS_LOOPBACK_MODE (1 << 1) +#define ISR_TXOK (1 << 1) +#define ISR_RXOK (1 << 4) + +#define ENABLE_ALL_FILTERS 0xffffffff +#define ENABLE_ALL_INTERRUPTS 0xffffffff + +/* We are sending one canfd message. */ +#define TX_READY_REG_VAL 0x1 + +#define FIRST_RX_STORE_INDEX 0x1 +#define STATUS_REG_MASK 0xf +#define DLC_FD_BIT_SHIFT 0x1b +#define DLC_FD_BIT_MASK 0xf8000000 +#define FIFO_STATUS_READ_INDEX_MASK 0x3f +#define FIFO_STATUS_FILL_LEVEL_MASK 0x7f00 +#define FILL_LEVEL_SHIFT 0x8 + +/* CANFD frame size ID, DLC and 16 DATA word. */ +#define CANFD_FRAME_SIZE 18 +/* CAN frame size ID, DLC and 2 DATA word. */ +#define CAN_FRAME_SIZE 4 + +/* Set the filters for CANFD controller. */ +static void enable_filters(QTestState *qts) +{ + const uint32_t arr_afmr[32] = { 0xb423deaa, 0xa2a40bdc, 0x1b64f486, + 0x95c0d4ee, 0xe0c44528, 0x4b407904, + 0xd2673f46, 0x9fc638d6, 0x8844f3d8, + 0xa607d1e8, 0x67871bf4, 0xc2557dc, + 0x9ea5b53e, 0x3643c0cc, 0x5a05ea8e, + 0x83a46d84, 0x4a25c2b8, 0x93a66008, + 0x2e467470, 0xedc66118, 0x9086f9f2, + 0xfa23dd36, 0xb6654b90, 0xb221b8ca, + 0x3467d1e2, 0xa3a55542, 0x5b26a012, + 0x2281ea7e, 0xcea0ece8, 0xdc61e588, + 0x2e5676a, 0x16821320 }; + + const uint32_t arr_afir[32] = { 0xa833dfa1, 0x255a477e, 0x3a4bb1c5, + 0x8f560a6c, 0x27f38903, 0x2fecec4d, + 0xa014c66d, 0xec289b8, 0x7e52dead, + 0x82e94f3c, 0xcf3e3c5c, 0x66059871, + 0x3f213df4, 0x25ac3959, 0xa12e9bef, + 0xa3ad3af, 0xbafd7fe, 0xb3cb40fd, + 0x5d9caa81, 0x2ed61902, 0x7cd64a0, + 0x4b1fa538, 0x9b5ced8c, 0x150de059, + 0xd2794227, 0x635e820a, 0xbb6b02cf, + 0xbb58176, 0x570025bb, 0xa78d9658, + 0x49d735df, 0xe5399d2f }; + + /* Passing the respective array values to all the AFMR and AFIR pairs. */ + for (int i = 0; i < 32; i++) { + /* For CANFD0. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_AFMR_REGISTER0 + 8 * i, + arr_afmr[i]); + qtest_writel(qts, CANFD0_BASE_ADDR + R_AFIR_REGISTER0 + 8 * i, + arr_afir[i]); + + /* For CANFD1. */ + qtest_writel(qts, CANFD1_BASE_ADDR + R_AFMR_REGISTER0 + 8 * i, + arr_afmr[i]); + qtest_writel(qts, CANFD1_BASE_ADDR + R_AFIR_REGISTER0 + 8 * i, + arr_afir[i]); + } + + /* Enable all the pairs from AFR register. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_FILTER_CONTROL_REGISTER, + ENABLE_ALL_FILTERS); + qtest_writel(qts, CANFD1_BASE_ADDR + R_FILTER_CONTROL_REGISTER, + ENABLE_ALL_FILTERS); +} + +static void configure_canfd(QTestState *qts, uint8_t mode) +{ + uint32_t status = 0; + + /* Put CANFD0 and CANFD1 in config mode. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_SRR_OFFSET, SRR_CONFIG_MODE); + qtest_writel(qts, CANFD1_BASE_ADDR + R_SRR_OFFSET, SRR_CONFIG_MODE); + + /* Write mode of operation in Mode select register. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_MSR_OFFSET, mode); + qtest_writel(qts, CANFD1_BASE_ADDR + R_MSR_OFFSET, mode); + + enable_filters(qts); + + /* Check here if CANFD0 and CANFD1 are in config mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_CONFIG_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_CONFIG_MODE); + + qtest_writel(qts, CANFD1_BASE_ADDR + R_IER_OFFSET, ENABLE_ALL_INTERRUPTS); + qtest_writel(qts, CANFD1_BASE_ADDR + R_IER_OFFSET, ENABLE_ALL_INTERRUPTS); + + qtest_writel(qts, CANFD0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CANFD); + qtest_writel(qts, CANFD1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CANFD); +} + +static void generate_random_data(uint32_t *buf_tx, bool is_canfd_frame) +{ + /* Generate random TX data for CANFD frame. */ + if (is_canfd_frame) { + for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { + buf_tx[2 + i] = rand(); + } + } else { + /* Generate random TX data for CAN frame. */ + for (int i = 0; i < CAN_FRAME_SIZE - 2; i++) { + buf_tx[2 + i] = rand(); + } + } +} + +static void read_data(QTestState *qts, uint64_t can_base_addr, uint32_t *buf_rx) +{ + uint32_t int_status; + uint32_t fifo_status_reg_value; + /* At which RX FIFO the received data is stored. */ + uint8_t store_ind = 0; + bool is_canfd_frame = false; + + /* Read the interrupt on CANFD rx. */ + int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_RXOK; + + g_assert_cmpint(int_status, ==, ISR_RXOK); + + /* Find the fill level and read index. */ + fifo_status_reg_value = qtest_readl(qts, can_base_addr + + RX_FIFO_STATUS_REGISTER); + + store_ind = (fifo_status_reg_value & FIFO_STATUS_READ_INDEX_MASK) + + ((fifo_status_reg_value & FIFO_STATUS_FILL_LEVEL_MASK) >> + FILL_LEVEL_SHIFT); + + g_assert_cmpint(store_ind, ==, FIRST_RX_STORE_INDEX); + + /* Read the RX register data for CANFD. */ + buf_rx[0] = qtest_readl(qts, can_base_addr + R_RX0_ID_OFFSET); + buf_rx[1] = qtest_readl(qts, can_base_addr + R_RX0_DLC_OFFSET); + + is_canfd_frame = (buf_rx[1] >> DLC_FD_BIT_SHIFT) & 1; + + if (is_canfd_frame) { + for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { + buf_rx[i + 2] = qtest_readl(qts, + can_base_addr + R_RX0_DATA1_OFFSET + 4 * i); + } + } else { + buf_rx[2] = qtest_readl(qts, can_base_addr + R_RX0_DATA1_OFFSET); + buf_rx[3] = qtest_readl(qts, can_base_addr + R_RX0_DATA2_OFFSET); + } + + /* Clear the RX interrupt. */ + qtest_writel(qts, CANFD1_BASE_ADDR + R_ICR_OFFSET, ISR_RXOK); +} + +static void write_data(QTestState *qts, uint64_t can_base_addr, + const uint32_t *buf_tx, bool is_canfd_frame) +{ + /* Write the TX register data for CANFD. */ + qtest_writel(qts, can_base_addr + R_TXID_OFFSET, buf_tx[0]); + qtest_writel(qts, can_base_addr + R_TXDLC_OFFSET, buf_tx[1]); + + if (is_canfd_frame) { + for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { + qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET + 4 * i, + buf_tx[2 + i]); + } + } else { + qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET, buf_tx[2]); + qtest_writel(qts, can_base_addr + R_TXDATA2_OFFSET, buf_tx[3]); + } +} + +static void send_data(QTestState *qts, uint64_t can_base_addr) +{ + uint32_t int_status; + + qtest_writel(qts, can_base_addr + R_TX_READY_REQ_REGISTER, + TX_READY_REG_VAL); + + /* Read the interrupt on CANFD for tx. */ + int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_TXOK; + + g_assert_cmpint(int_status, ==, ISR_TXOK); + + /* Clear the interrupt for tx. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_ICR_OFFSET, ISR_TXOK); +} + +static void match_rx_tx_data(const uint32_t *buf_tx, const uint32_t *buf_rx, + bool is_canfd_frame) +{ + uint16_t size = 0; + uint8_t len = CAN_FRAME_SIZE; + + if (is_canfd_frame) { + len = CANFD_FRAME_SIZE; + } + + while (size < len) { + if (R_RX0_ID_OFFSET + 4 * size == R_RX0_DLC_OFFSET) { + g_assert_cmpint((buf_rx[size] & DLC_FD_BIT_MASK), ==, + (buf_tx[size] & DLC_FD_BIT_MASK)); + } else { + if (!is_canfd_frame && size == 4) { + break; + } + + g_assert_cmpint(buf_rx[size], ==, buf_tx[size]); + } + + size++; + } +} +/* + * Xilinx CANFD supports both CAN and CANFD frames. This test will be + * transferring CAN frame i.e. 8 bytes of data from CANFD0 and CANFD1 through + * canbus. CANFD0 initiate the data transfer to can-bus, CANFD1 receives the + * data. Test compares the can frame data sent from CANFD0 and received on + * CANFD1. + */ +static void test_can_data_transfer(void) +{ + uint32_t buf_tx[CAN_FRAME_SIZE] = { 0x5a5bb9a4, 0x80000000, + 0x12345678, 0x87654321 }; + uint32_t buf_rx[CAN_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; + uint32_t status = 0; + + generate_random_data(buf_tx, false); + + QTestState *qts = qtest_init("-machine xlnx-versal-virt" + " -object can-bus,id=canbus" + " -machine canbus0=canbus" + " -machine canbus1=canbus" + ); + + configure_canfd(qts, MSR_NORMAL_MODE); + + /* Check if CANFD0 and CANFD1 are in Normal mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + write_data(qts, CANFD0_BASE_ADDR, buf_tx, false); + + send_data(qts, CANFD0_BASE_ADDR); + read_data(qts, CANFD1_BASE_ADDR, buf_rx); + match_rx_tx_data(buf_tx, buf_rx, false); + + qtest_quit(qts); +} + +/* + * This test will be transferring CANFD frame i.e. 64 bytes of data from CANFD0 + * and CANFD1 through canbus. CANFD0 initiate the data transfer to can-bus, + * CANFD1 receives the data. Test compares the CANFD frame data sent from CANFD0 + * with received on CANFD1. + */ +static void test_canfd_data_transfer(void) +{ + uint32_t buf_tx[CANFD_FRAME_SIZE] = { 0x5a5bb9a4, 0xf8000000 }; + uint32_t buf_rx[CANFD_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; + uint32_t status = 0; + + generate_random_data(buf_tx, true); + + QTestState *qts = qtest_init("-machine xlnx-versal-virt" + " -object can-bus,id=canbus" + " -machine canbus0=canbus" + " -machine canbus1=canbus" + ); + + configure_canfd(qts, MSR_NORMAL_MODE); + + /* Check if CANFD0 and CANFD1 are in Normal mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + write_data(qts, CANFD0_BASE_ADDR, buf_tx, true); + + send_data(qts, CANFD0_BASE_ADDR); + read_data(qts, CANFD1_BASE_ADDR, buf_rx); + match_rx_tx_data(buf_tx, buf_rx, true); + + qtest_quit(qts); +} + +/* + * This test is performing loopback mode on CANFD0 and CANFD1. Data sent from + * TX of each CANFD0 and CANFD1 are compared with RX register data for + * respective CANFD Controller. + */ +static void test_can_loopback(void) +{ + uint32_t buf_tx[CANFD_FRAME_SIZE] = { 0x5a5bb9a4, 0xf8000000 }; + uint32_t buf_rx[CANFD_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; + uint32_t status = 0; + + generate_random_data(buf_tx, true); + + QTestState *qts = qtest_init("-machine xlnx-versal-virt" + " -object can-bus,id=canbus" + " -machine canbus0=canbus" + " -machine canbus1=canbus" + ); + + configure_canfd(qts, MSR_LOOPBACK_MODE); + + /* Check if CANFD0 and CANFD1 are set in correct loopback mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE); + + write_data(qts, CANFD0_BASE_ADDR, buf_tx, true); + + send_data(qts, CANFD0_BASE_ADDR); + read_data(qts, CANFD0_BASE_ADDR, buf_rx); + match_rx_tx_data(buf_tx, buf_rx, true); + + generate_random_data(buf_tx, true); + + write_data(qts, CANFD1_BASE_ADDR, buf_tx, true); + + send_data(qts, CANFD1_BASE_ADDR); + read_data(qts, CANFD1_BASE_ADDR, buf_rx); + match_rx_tx_data(buf_tx, buf_rx, true); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/net/canfd/can_data_transfer", test_can_data_transfer); + qtest_add_func("/net/canfd/canfd_data_transfer", test_canfd_data_transfer); + qtest_add_func("/net/canfd/can_loopback", test_can_loopback); + + return g_test_run(); +} From 8d9006aeca58e4635d58fdd620d52fe77c9eb00d Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:31 +0100 Subject: [PATCH 140/412] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support Allwinner R40 (sun8i) SoC features a Quad-Core Cortex-A7 ARM CPU, and a Mali400 MP2 GPU from ARM. It's also known as the Allwinner T3 for In-Car Entertainment usage, A40i and A40pro are variants that differ in applicable temperatures range (industrial and military). Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 10 + hw/arm/allwinner-r40.c | 415 +++++++++++++++++++++++++++++++++ hw/arm/bananapi_m2u.c | 129 ++++++++++ hw/arm/meson.build | 1 + include/hw/arm/allwinner-r40.h | 110 +++++++++ 5 files changed, 665 insertions(+) create mode 100644 hw/arm/allwinner-r40.c create mode 100644 hw/arm/bananapi_m2u.c create mode 100644 include/hw/arm/allwinner-r40.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index acc4371a4a..02b2d8167d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -403,6 +403,16 @@ config ALLWINNER_H3 select USB_EHCI_SYSBUS select SD +config ALLWINNER_R40 + bool + default y if TCG && ARM + select ALLWINNER_A10_PIT + select SERIAL + select ARM_TIMER + select ARM_GIC + select UNIMP + select SD + config RASPI bool default y diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c new file mode 100644 index 0000000000..97f2aa92fd --- /dev/null +++ b/hw/arm/allwinner-r40.c @@ -0,0 +1,415 @@ +/* + * Allwinner R40/A40i/T3 System on Chip emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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 "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/bswap.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "hw/qdev-core.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/misc/unimp.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/loader.h" +#include "sysemu/sysemu.h" +#include "hw/arm/allwinner-r40.h" + +/* Memory map */ +const hwaddr allwinner_r40_memmap[] = { + [AW_R40_DEV_SRAM_A1] = 0x00000000, + [AW_R40_DEV_SRAM_A2] = 0x00004000, + [AW_R40_DEV_SRAM_A3] = 0x00008000, + [AW_R40_DEV_SRAM_A4] = 0x0000b400, + [AW_R40_DEV_MMC0] = 0x01c0f000, + [AW_R40_DEV_MMC1] = 0x01c10000, + [AW_R40_DEV_MMC2] = 0x01c11000, + [AW_R40_DEV_MMC3] = 0x01c12000, + [AW_R40_DEV_PIT] = 0x01c20c00, + [AW_R40_DEV_UART0] = 0x01c28000, + [AW_R40_DEV_GIC_DIST] = 0x01c81000, + [AW_R40_DEV_GIC_CPU] = 0x01c82000, + [AW_R40_DEV_GIC_HYP] = 0x01c84000, + [AW_R40_DEV_GIC_VCPU] = 0x01c86000, + [AW_R40_DEV_SDRAM] = 0x40000000 +}; + +/* List of unimplemented devices */ +struct AwR40Unimplemented { + const char *device_name; + hwaddr base; + hwaddr size; +}; + +static struct AwR40Unimplemented r40_unimplemented[] = { + { "d-engine", 0x01000000, 4 * MiB }, + { "d-inter", 0x01400000, 128 * KiB }, + { "sram-c", 0x01c00000, 4 * KiB }, + { "dma", 0x01c02000, 4 * KiB }, + { "nfdc", 0x01c03000, 4 * KiB }, + { "ts", 0x01c04000, 4 * KiB }, + { "spi0", 0x01c05000, 4 * KiB }, + { "spi1", 0x01c06000, 4 * KiB }, + { "cs0", 0x01c09000, 4 * KiB }, + { "keymem", 0x01c0a000, 4 * KiB }, + { "emac", 0x01c0b000, 4 * KiB }, + { "usb0-otg", 0x01c13000, 4 * KiB }, + { "usb0-host", 0x01c14000, 4 * KiB }, + { "crypto", 0x01c15000, 4 * KiB }, + { "spi2", 0x01c17000, 4 * KiB }, + { "sata", 0x01c18000, 4 * KiB }, + { "usb1-host", 0x01c19000, 4 * KiB }, + { "sid", 0x01c1b000, 4 * KiB }, + { "usb2-host", 0x01c1c000, 4 * KiB }, + { "cs1", 0x01c1d000, 4 * KiB }, + { "spi3", 0x01c1f000, 4 * KiB }, + { "ccu", 0x01c20000, 1 * KiB }, + { "rtc", 0x01c20400, 1 * KiB }, + { "pio", 0x01c20800, 1 * KiB }, + { "owa", 0x01c21000, 1 * KiB }, + { "ac97", 0x01c21400, 1 * KiB }, + { "cir0", 0x01c21800, 1 * KiB }, + { "cir1", 0x01c21c00, 1 * KiB }, + { "pcm0", 0x01c22000, 1 * KiB }, + { "pcm1", 0x01c22400, 1 * KiB }, + { "pcm2", 0x01c22800, 1 * KiB }, + { "audio", 0x01c22c00, 1 * KiB }, + { "keypad", 0x01c23000, 1 * KiB }, + { "pwm", 0x01c23400, 1 * KiB }, + { "keyadc", 0x01c24400, 1 * KiB }, + { "ths", 0x01c24c00, 1 * KiB }, + { "rtp", 0x01c25000, 1 * KiB }, + { "pmu", 0x01c25400, 1 * KiB }, + { "cpu-cfg", 0x01c25c00, 1 * KiB }, + { "uart0", 0x01c28000, 1 * KiB }, + { "uart1", 0x01c28400, 1 * KiB }, + { "uart2", 0x01c28800, 1 * KiB }, + { "uart3", 0x01c28c00, 1 * KiB }, + { "uart4", 0x01c29000, 1 * KiB }, + { "uart5", 0x01c29400, 1 * KiB }, + { "uart6", 0x01c29800, 1 * KiB }, + { "uart7", 0x01c29c00, 1 * KiB }, + { "ps20", 0x01c2a000, 1 * KiB }, + { "ps21", 0x01c2a400, 1 * KiB }, + { "twi0", 0x01c2ac00, 1 * KiB }, + { "twi1", 0x01c2b000, 1 * KiB }, + { "twi2", 0x01c2b400, 1 * KiB }, + { "twi3", 0x01c2b800, 1 * KiB }, + { "twi4", 0x01c2c000, 1 * KiB }, + { "scr", 0x01c2c400, 1 * KiB }, + { "tvd-top", 0x01c30000, 4 * KiB }, + { "tvd0", 0x01c31000, 4 * KiB }, + { "tvd1", 0x01c32000, 4 * KiB }, + { "tvd2", 0x01c33000, 4 * KiB }, + { "tvd3", 0x01c34000, 4 * KiB }, + { "gpu", 0x01c40000, 64 * KiB }, + { "gmac", 0x01c50000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "dram-com", 0x01c62000, 4 * KiB }, + { "dram-ctl", 0x01c63000, 4 * KiB }, + { "tcon-top", 0x01c70000, 4 * KiB }, + { "lcd0", 0x01c71000, 4 * KiB }, + { "lcd1", 0x01c72000, 4 * KiB }, + { "tv0", 0x01c73000, 4 * KiB }, + { "tv1", 0x01c74000, 4 * KiB }, + { "tve-top", 0x01c90000, 16 * KiB }, + { "tve0", 0x01c94000, 16 * KiB }, + { "tve1", 0x01c98000, 16 * KiB }, + { "mipi_dsi", 0x01ca0000, 4 * KiB }, + { "mipi_dphy", 0x01ca1000, 4 * KiB }, + { "ve", 0x01d00000, 1024 * KiB }, + { "mp", 0x01e80000, 128 * KiB }, + { "hdmi", 0x01ee0000, 128 * KiB }, + { "prcm", 0x01f01400, 1 * KiB }, + { "debug", 0x3f500000, 64 * KiB }, + { "cpubist", 0x3f501000, 4 * KiB }, + { "dcu", 0x3fff0000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "brom", 0xffff0000, 36 * KiB } +}; + +/* Per Processor Interrupts */ +enum { + AW_R40_GIC_PPI_MAINT = 9, + AW_R40_GIC_PPI_HYPTIMER = 10, + AW_R40_GIC_PPI_VIRTTIMER = 11, + AW_R40_GIC_PPI_SECTIMER = 13, + AW_R40_GIC_PPI_PHYSTIMER = 14 +}; + +/* Shared Processor Interrupts */ +enum { + AW_R40_GIC_SPI_UART0 = 1, + AW_R40_GIC_SPI_TIMER0 = 22, + AW_R40_GIC_SPI_TIMER1 = 23, + AW_R40_GIC_SPI_MMC0 = 32, + AW_R40_GIC_SPI_MMC1 = 33, + AW_R40_GIC_SPI_MMC2 = 34, + AW_R40_GIC_SPI_MMC3 = 35, +}; + +/* Allwinner R40 general constants */ +enum { + AW_R40_GIC_NUM_SPI = 128 +}; + +#define BOOT0_MAGIC "eGON.BT0" + +/* The low 8-bits of the 'boot_media' field in the SPL header */ +#define SUNXI_BOOTED_FROM_MMC0 0 +#define SUNXI_BOOTED_FROM_NAND 1 +#define SUNXI_BOOTED_FROM_MMC2 2 +#define SUNXI_BOOTED_FROM_SPI 3 + +struct boot_file_head { + uint32_t b_instruction; + uint8_t magic[8]; + uint32_t check_sum; + uint32_t length; + uint32_t pub_head_size; + uint32_t fel_script_address; + uint32_t fel_uEnv_length; + uint32_t dt_name_offset; + uint32_t dram_size; + uint32_t boot_media; + uint32_t string_pool[13]; +}; + +bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit) +{ + const int64_t rom_size = 32 * KiB; + g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); + struct boot_file_head *head = (struct boot_file_head *)buffer; + + if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { + error_setg(&error_fatal, "%s: failed to read BlockBackend data", + __func__); + return false; + } + + /* we only check the magic string here. */ + if (memcmp(head->magic, BOOT0_MAGIC, sizeof(head->magic))) { + return false; + } + + /* + * Simulate the behavior of the bootROM, it will change the boot_media + * flag to indicate where the chip is booting from. R40 can boot from + * mmc0 or mmc2, the default value of boot_media is zero + * (SUNXI_BOOTED_FROM_MMC0), let's fix this flag when it is booting from + * the others. + */ + if (unit == 2) { + head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2); + } else { + head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0); + } + + rom_add_blob("allwinner-r40.bootrom", buffer, rom_size, + rom_size, s->memmap[AW_R40_DEV_SRAM_A1], + NULL, NULL, NULL, NULL, false); + return true; +} + +static void allwinner_r40_init(Object *obj) +{ + static const char *mmc_names[AW_R40_NUM_MMCS] = { + "mmc0", "mmc1", "mmc2", "mmc3" + }; + AwR40State *s = AW_R40(obj); + + s->memmap = allwinner_r40_memmap; + + for (int i = 0; i < AW_R40_NUM_CPUS; i++) { + object_initialize_child(obj, "cpu[*]", &s->cpus[i], + ARM_CPU_TYPE_NAME("cortex-a7")); + } + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC); + + object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT); + object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer), + "clk0-freq"); + object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer), + "clk1-freq"); + + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { + object_initialize_child(obj, mmc_names[i], &s->mmc[i], + TYPE_AW_SDHOST_SUN5I); + } +} + +static void allwinner_r40_realize(DeviceState *dev, Error **errp) +{ + AwR40State *s = AW_R40(dev); + unsigned i; + + /* CPUs */ + for (i = 0; i < AW_R40_NUM_CPUS; i++) { + + /* + * Disable secondary CPUs. Guest EL3 firmware will start + * them via CPU reset control registers. + */ + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off", + i > 0); + + /* All exception levels required */ + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el3", true); + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el2", true); + + /* Mark realized */ + qdev_realize(DEVICE(&s->cpus[i]), NULL, &error_fatal); + } + + /* Generic Interrupt Controller */ + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_R40_GIC_NUM_SPI + + GIC_INTERNAL); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_R40_NUM_CPUS); + qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false); + qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true); + sysbus_realize(SYS_BUS_DEVICE(&s->gic), &error_fatal); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_R40_DEV_GIC_DIST]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_R40_DEV_GIC_CPU]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_R40_DEV_GIC_HYP]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_R40_DEV_GIC_VCPU]); + + /* + * Wire the outputs from each CPU's generic timer and the GICv2 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < AW_R40_NUM_CPUS; i++) { + DeviceState *cpudev = DEVICE(&s->cpus[i]); + int ppibase = AW_R40_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_SGIS; + int irq; + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs used for this board. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = AW_R40_GIC_PPI_PHYSTIMER, + [GTIMER_VIRT] = AW_R40_GIC_PPI_VIRTTIMER, + [GTIMER_HYP] = AW_R40_GIC_PPI_HYPTIMER, + [GTIMER_SEC] = AW_R40_GIC_PPI_SECTIMER, + }; + + /* Connect CPU timer outputs to GIC PPI inputs */ + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + timer_irq[irq])); + } + + /* Connect GIC outputs to CPU interrupt inputs */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_R40_NUM_CPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_R40_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_R40_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + /* GIC maintenance signal */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_R40_NUM_CPUS), + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + AW_R40_GIC_PPI_MAINT)); + } + + /* Timer */ + sysbus_realize(SYS_BUS_DEVICE(&s->timer), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_R40_DEV_PIT]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0, + qdev_get_gpio_in(DEVICE(&s->gic), + AW_R40_GIC_SPI_TIMER0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1, + qdev_get_gpio_in(DEVICE(&s->gic), + AW_R40_GIC_SPI_TIMER1)); + + /* SRAM */ + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", + 16 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", + 16 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a3, OBJECT(dev), "sram A3", + 13 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a4, OBJECT(dev), "sram A4", + 3 * KiB, &error_abort); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A1], &s->sram_a1); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A2], &s->sram_a2); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A3], &s->sram_a3); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A4], &s->sram_a4); + + /* SD/MMC */ + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic), + AW_R40_GIC_SPI_MMC0 + i); + const hwaddr addr = s->memmap[AW_R40_DEV_MMC0 + i]; + + object_property_set_link(OBJECT(&s->mmc[i]), "dma-memory", + OBJECT(get_system_memory()), &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i]), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[i]), 0, addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc[i]), 0, irq); + } + + /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ + serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART0], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0), + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { + create_unimplemented_device(r40_unimplemented[i].device_name, + r40_unimplemented[i].base, + r40_unimplemented[i].size); + } +} + +static void allwinner_r40_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = allwinner_r40_realize; + /* Reason: uses serial_hd() in realize function */ + dc->user_creatable = false; +} + +static const TypeInfo allwinner_r40_type_info = { + .name = TYPE_AW_R40, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwR40State), + .instance_init = allwinner_r40_init, + .class_init = allwinner_r40_class_init, +}; + +static void allwinner_r40_register_types(void) +{ + type_register_static(&allwinner_r40_type_info); +} + +type_init(allwinner_r40_register_types) diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c new file mode 100644 index 0000000000..1d49a006b5 --- /dev/null +++ b/hw/arm/bananapi_m2u.c @@ -0,0 +1,129 @@ +/* + * Bananapi M2U emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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/units.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/arm/allwinner-r40.h" + +static struct arm_boot_info bpim2u_binfo; + +/* + * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one is + * connected to sdcard and another mount an emmc media. + * Attach the mmc driver and try loading bootloader. + */ +static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit, + bool load_bootroom, bool *bootroom_loaded) +{ + DriveInfo *di = drive_get(IF_SD, 0, unit); + BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; + BusState *bus; + DeviceState *carddev; + + bus = qdev_get_child_bus(DEVICE(mmc), "sd-bus"); + if (bus == NULL) { + error_report("No SD bus found in SOC object"); + exit(1); + } + + carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); + + if (load_bootroom && blk && blk_is_available(blk)) { + /* Use Boot ROM to copy data from SD card to SRAM */ + *bootroom_loaded = allwinner_r40_bootrom_setup(s, blk, unit); + } +} + +static void bpim2u_init(MachineState *machine) +{ + bool bootroom_loaded = false; + AwR40State *r40; + + /* BIOS is not supported by this board */ + if (machine->firmware) { + error_report("BIOS not supported for this machine"); + exit(1); + } + + /* Only allow Cortex-A7 for this board */ + if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) { + error_report("This board can only be used with cortex-a7 CPU"); + exit(1); + } + + r40 = AW_R40(object_new(TYPE_AW_R40)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(r40)); + object_unref(OBJECT(r40)); + + /* Setup timer properties */ + object_property_set_int(OBJECT(r40), "clk0-freq", 32768, &error_abort); + object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000, + &error_abort); + + /* Mark R40 object realized */ + qdev_realize(DEVICE(r40), NULL, &error_abort); + + /* + * Plug in SD card and try load bootrom, R40 has 4 mmc controllers but can + * only booting from mmc0 and mmc2. + */ + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { + switch (i) { + case 0: + case 2: + mmc_attach_drive(r40, &r40->mmc[i], i, + !machine->kernel_filename && !bootroom_loaded, + &bootroom_loaded); + break; + default: + mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL); + break; + } + } + + /* SDRAM */ + memory_region_add_subregion(get_system_memory(), + r40->memmap[AW_R40_DEV_SDRAM], machine->ram); + + bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; + bpim2u_binfo.ram_size = machine->ram_size; + bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_binfo); +} + +static void bpim2u_machine_init(MachineClass *mc) +{ + mc->desc = "Bananapi M2U (Cortex-A7)"; + mc->init = bpim2u_init; + mc->min_cpus = AW_R40_NUM_CPUS; + mc->max_cpus = AW_R40_NUM_CPUS; + mc->default_cpus = AW_R40_NUM_CPUS; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + mc->default_ram_size = 1 * GiB; + mc->default_ram_id = "bpim2u.ram"; +} + +DEFINE_MACHINE("bpim2u", bpim2u_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index b545ba0e4f..870ec67376 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -37,6 +37,7 @@ arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c', 'omap2.c')) arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) +arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c')) arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h new file mode 100644 index 0000000000..348bf25d6b --- /dev/null +++ b/include/hw/arm/allwinner-r40.h @@ -0,0 +1,110 @@ +/* + * Allwinner R40/A40i/T3 System on Chip emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_ALLWINNER_R40_H +#define HW_ARM_ALLWINNER_R40_H + +#include "qom/object.h" +#include "hw/arm/boot.h" +#include "hw/timer/allwinner-a10-pit.h" +#include "hw/intc/arm_gic.h" +#include "hw/sd/allwinner-sdhost.h" +#include "target/arm/cpu.h" +#include "sysemu/block-backend.h" + +enum { + AW_R40_DEV_SRAM_A1, + AW_R40_DEV_SRAM_A2, + AW_R40_DEV_SRAM_A3, + AW_R40_DEV_SRAM_A4, + AW_R40_DEV_MMC0, + AW_R40_DEV_MMC1, + AW_R40_DEV_MMC2, + AW_R40_DEV_MMC3, + AW_R40_DEV_CCU, + AW_R40_DEV_PIT, + AW_R40_DEV_UART0, + AW_R40_DEV_GIC_DIST, + AW_R40_DEV_GIC_CPU, + AW_R40_DEV_GIC_HYP, + AW_R40_DEV_GIC_VCPU, + AW_R40_DEV_SDRAM +}; + +#define AW_R40_NUM_CPUS (4) + +/** + * Allwinner R40 object model + * @{ + */ + +/** Object type for the Allwinner R40 SoC */ +#define TYPE_AW_R40 "allwinner-r40" + +/** Convert input object to Allwinner R40 state object */ +OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40) + +/** @} */ + +/** + * Allwinner R40 object + * + * This struct contains the state of all the devices + * which are currently emulated by the R40 SoC code. + */ +#define AW_R40_NUM_MMCS 4 + +struct AwR40State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + ARMCPU cpus[AW_R40_NUM_CPUS]; + const hwaddr *memmap; + AwA10PITState timer; + AwSdHostState mmc[AW_R40_NUM_MMCS]; + GICState gic; + MemoryRegion sram_a1; + MemoryRegion sram_a2; + MemoryRegion sram_a3; + MemoryRegion sram_a4; +}; + +/** + * Emulate Boot ROM firmware setup functionality. + * + * A real Allwinner R40 SoC contains a Boot ROM + * which is the first code that runs right after + * the SoC is powered on. The Boot ROM is responsible + * for loading user code (e.g. a bootloader) from any + * of the supported external devices and writing the + * downloaded code to internal SRAM. After loading the SoC + * begins executing the code written to SRAM. + * + * This function emulates the Boot ROM by copying 32 KiB + * of data from the given block device and writes it to + * the start of the first internal SRAM memory. + * + * @s: Allwinner R40 state object pointer + * @blk: Block backend device object pointer + * @unit: the mmc control's unit + */ +bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit); + +#endif /* HW_ARM_ALLWINNER_R40_H */ From dc2a070d125772fe30384596d4d4ce6d9950b004 Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:31 +0100 Subject: [PATCH 141/412] hw/arm/allwinner-r40: add Clock Control Unit The CCU provides the registers to program the PLLs and the controls most of the clock generation, division, distribution, synchronization and gating. This commit adds support for the Clock Control Unit which emulates a simple read/write register interface. Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank Signed-off-by: Peter Maydell --- hw/arm/allwinner-r40.c | 8 +- hw/misc/allwinner-r40-ccu.c | 209 ++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + include/hw/arm/allwinner-r40.h | 2 + include/hw/misc/allwinner-r40-ccu.h | 65 +++++++++ 5 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 hw/misc/allwinner-r40-ccu.c create mode 100644 include/hw/misc/allwinner-r40-ccu.h diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 97f2aa92fd..72973f69ff 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -42,6 +42,7 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_MMC1] = 0x01c10000, [AW_R40_DEV_MMC2] = 0x01c11000, [AW_R40_DEV_MMC3] = 0x01c12000, + [AW_R40_DEV_CCU] = 0x01c20000, [AW_R40_DEV_PIT] = 0x01c20c00, [AW_R40_DEV_UART0] = 0x01c28000, [AW_R40_DEV_GIC_DIST] = 0x01c81000, @@ -80,7 +81,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = { { "usb2-host", 0x01c1c000, 4 * KiB }, { "cs1", 0x01c1d000, 4 * KiB }, { "spi3", 0x01c1f000, 4 * KiB }, - { "ccu", 0x01c20000, 1 * KiB }, { "rtc", 0x01c20400, 1 * KiB }, { "pio", 0x01c20800, 1 * KiB }, { "owa", 0x01c21000, 1 * KiB }, @@ -250,6 +250,8 @@ static void allwinner_r40_init(Object *obj) object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer), "clk1-freq"); + object_initialize_child(obj, "ccu", &s->ccu, TYPE_AW_R40_CCU); + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { object_initialize_child(obj, mmc_names[i], &s->mmc[i], TYPE_AW_SDHOST_SUN5I); @@ -364,6 +366,10 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A4], &s->sram_a4); + /* Clock Control Unit */ + sysbus_realize(SYS_BUS_DEVICE(&s->ccu), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_R40_DEV_CCU]); + /* SD/MMC */ for (int i = 0; i < AW_R40_NUM_MMCS; i++) { qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic), diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c new file mode 100644 index 0000000000..d82fee12db --- /dev/null +++ b/hw/misc/allwinner-r40-ccu.c @@ -0,0 +1,209 @@ +/* + * Allwinner R40 Clock Control Unit emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-r40-ccu.h" + +/* CCU register offsets */ +enum { + REG_PLL_CPUX_CTRL = 0x0000, + REG_PLL_AUDIO_CTRL = 0x0008, + REG_PLL_VIDEO0_CTRL = 0x0010, + REG_PLL_VE_CTRL = 0x0018, + REG_PLL_DDR0_CTRL = 0x0020, + REG_PLL_PERIPH0_CTRL = 0x0028, + REG_PLL_PERIPH1_CTRL = 0x002c, + REG_PLL_VIDEO1_CTRL = 0x0030, + REG_PLL_SATA_CTRL = 0x0034, + REG_PLL_GPU_CTRL = 0x0038, + REG_PLL_MIPI_CTRL = 0x0040, + REG_PLL_DE_CTRL = 0x0048, + REG_PLL_DDR1_CTRL = 0x004c, + REG_AHB1_APB1_CFG = 0x0054, + REG_APB2_CFG = 0x0058, + REG_MMC0_CLK = 0x0088, + REG_MMC1_CLK = 0x008c, + REG_MMC2_CLK = 0x0090, + REG_MMC3_CLK = 0x0094, + REG_USBPHY_CFG = 0x00cc, + REG_PLL_DDR_AUX = 0x00f0, + REG_DRAM_CFG = 0x00f4, + REG_PLL_DDR1_CFG = 0x00f8, + REG_DRAM_CLK_GATING = 0x0100, + REG_GMAC_CLK = 0x0164, + REG_SYS_32K_CLK = 0x0310, + REG_PLL_LOCK_CTRL = 0x0320, +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCU register flags */ +enum { + REG_PLL_ENABLE = (1 << 31), + REG_PLL_LOCK = (1 << 28), +}; + +static uint64_t allwinner_r40_ccu_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40ClockCtlState *s = AW_R40_CCU(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case 0x324 ... AW_R40_CCU_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_r40_ccu_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40ClockCtlState *s = AW_R40_CCU(opaque); + + switch (offset) { + case REG_DRAM_CFG: /* DRAM Configuration(for DDR0) */ + /* bit16: SDRCLK_UPD (SDRCLK configuration 0 update) */ + val &= ~(1 << 16); + break; + case REG_PLL_DDR1_CTRL: /* DDR1 Control register */ + /* bit30: SDRPLL_UPD */ + val &= ~(1 << 30); + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + case REG_PLL_CPUX_CTRL: + case REG_PLL_AUDIO_CTRL: + case REG_PLL_VE_CTRL: + case REG_PLL_VIDEO0_CTRL: + case REG_PLL_DDR0_CTRL: + case REG_PLL_PERIPH0_CTRL: + case REG_PLL_PERIPH1_CTRL: + case REG_PLL_VIDEO1_CTRL: + case REG_PLL_SATA_CTRL: + case REG_PLL_GPU_CTRL: + case REG_PLL_MIPI_CTRL: + case REG_PLL_DE_CTRL: + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + case 0x324 ... AW_R40_CCU_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } + + s->regs[REG_INDEX(offset)] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_r40_ccu_ops = { + .read = allwinner_r40_ccu_read, + .write = allwinner_r40_ccu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_r40_ccu_reset(DeviceState *dev) +{ + AwR40ClockCtlState *s = AW_R40_CCU(dev); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_PLL_CPUX_CTRL)] = 0x00001000; + s->regs[REG_INDEX(REG_PLL_AUDIO_CTRL)] = 0x00035514; + s->regs[REG_INDEX(REG_PLL_VIDEO0_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_VE_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_DDR0_CTRL)] = 0x00001000, + s->regs[REG_INDEX(REG_PLL_PERIPH0_CTRL)] = 0x00041811; + s->regs[REG_INDEX(REG_PLL_PERIPH1_CTRL)] = 0x00041811; + s->regs[REG_INDEX(REG_PLL_VIDEO1_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_SATA_CTRL)] = 0x00001811; + s->regs[REG_INDEX(REG_PLL_GPU_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_MIPI_CTRL)] = 0x00000515; + s->regs[REG_INDEX(REG_PLL_DE_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_DDR1_CTRL)] = 0x00001800; + s->regs[REG_INDEX(REG_AHB1_APB1_CFG)] = 0x00001010; + s->regs[REG_INDEX(REG_APB2_CFG)] = 0x01000000; + s->regs[REG_INDEX(REG_PLL_DDR_AUX)] = 0x00000001; + s->regs[REG_INDEX(REG_PLL_DDR1_CFG)] = 0x0ccca000; + s->regs[REG_INDEX(REG_SYS_32K_CLK)] = 0x0000000f; +} + +static void allwinner_r40_ccu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwR40ClockCtlState *s = AW_R40_CCU(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_r40_ccu_ops, s, + TYPE_AW_R40_CCU, AW_R40_CCU_IOSIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_r40_ccu_vmstate = { + .name = "allwinner-r40-ccu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwR40ClockCtlState, AW_R40_CCU_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_r40_ccu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_r40_ccu_reset; + dc->vmsd = &allwinner_r40_ccu_vmstate; +} + +static const TypeInfo allwinner_r40_ccu_info = { + .name = TYPE_AW_R40_CCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_r40_ccu_init, + .instance_size = sizeof(AwR40ClockCtlState), + .class_init = allwinner_r40_ccu_class_init, +}; + +static void allwinner_r40_ccu_register(void) +{ + type_register_static(&allwinner_r40_ccu_info); +} + +type_init(allwinner_r40_ccu_register) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index a40245ad44..96e35f1cdb 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -44,6 +44,7 @@ specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c' softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) +softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c')) softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c')) softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 348bf25d6b..3be9dc962b 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -25,6 +25,7 @@ #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/arm_gic.h" #include "hw/sd/allwinner-sdhost.h" +#include "hw/misc/allwinner-r40-ccu.h" #include "target/arm/cpu.h" #include "sysemu/block-backend.h" @@ -79,6 +80,7 @@ struct AwR40State { const hwaddr *memmap; AwA10PITState timer; AwSdHostState mmc[AW_R40_NUM_MMCS]; + AwR40ClockCtlState ccu; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/misc/allwinner-r40-ccu.h b/include/hw/misc/allwinner-r40-ccu.h new file mode 100644 index 0000000000..ceb74eff92 --- /dev/null +++ b/include/hw/misc/allwinner-r40-ccu.h @@ -0,0 +1,65 @@ +/* + * Allwinner R40 Clock Control Unit emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_MISC_ALLWINNER_R40_CCU_H +#define HW_MISC_ALLWINNER_R40_CCU_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * @name Constants + * @{ + */ + +/** Size of register I/O address space used by CCU device */ +#define AW_R40_CCU_IOSIZE (0x400) + +/** Total number of known registers */ +#define AW_R40_CCU_REGS_NUM (AW_R40_CCU_IOSIZE / sizeof(uint32_t)) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_R40_CCU "allwinner-r40-ccu" +OBJECT_DECLARE_SIMPLE_TYPE(AwR40ClockCtlState, AW_R40_CCU) + +/** @} */ + +/** + * Allwinner R40 CCU object instance state. + */ +struct AwR40ClockCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_R40_CCU_REGS_NUM]; + +}; + +#endif /* HW_MISC_ALLWINNER_R40_CCU_H */ From d1e409c5831b2f48b285a4d00f84fbc6a3a927bb Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:32 +0100 Subject: [PATCH 142/412] hw: allwinner-r40: Complete uart devices R40 has eight UARTs, support both 16450 and 16550 compatible modes. Signed-off-by: qianfan Zhao Signed-off-by: Peter Maydell --- hw/arm/allwinner-r40.c | 34 +++++++++++++++++++++++++++++++--- include/hw/arm/allwinner-r40.h | 8 ++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 72973f69ff..537a90b23d 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -45,6 +45,13 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_CCU] = 0x01c20000, [AW_R40_DEV_PIT] = 0x01c20c00, [AW_R40_DEV_UART0] = 0x01c28000, + [AW_R40_DEV_UART1] = 0x01c28400, + [AW_R40_DEV_UART2] = 0x01c28800, + [AW_R40_DEV_UART3] = 0x01c28c00, + [AW_R40_DEV_UART4] = 0x01c29000, + [AW_R40_DEV_UART5] = 0x01c29400, + [AW_R40_DEV_UART6] = 0x01c29800, + [AW_R40_DEV_UART7] = 0x01c29c00, [AW_R40_DEV_GIC_DIST] = 0x01c81000, [AW_R40_DEV_GIC_CPU] = 0x01c82000, [AW_R40_DEV_GIC_HYP] = 0x01c84000, @@ -157,6 +164,13 @@ enum { /* Shared Processor Interrupts */ enum { AW_R40_GIC_SPI_UART0 = 1, + AW_R40_GIC_SPI_UART1 = 2, + AW_R40_GIC_SPI_UART2 = 3, + AW_R40_GIC_SPI_UART3 = 4, + AW_R40_GIC_SPI_UART4 = 17, + AW_R40_GIC_SPI_UART5 = 18, + AW_R40_GIC_SPI_UART6 = 19, + AW_R40_GIC_SPI_UART7 = 20, AW_R40_GIC_SPI_TIMER0 = 22, AW_R40_GIC_SPI_TIMER1 = 23, AW_R40_GIC_SPI_MMC0 = 32, @@ -384,9 +398,23 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) } /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ - serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART0], 2, - qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0), - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + for (int i = 0; i < AW_R40_NUM_UARTS; i++) { + static const int uart_irqs[AW_R40_NUM_UARTS] = { + AW_R40_GIC_SPI_UART0, + AW_R40_GIC_SPI_UART1, + AW_R40_GIC_SPI_UART2, + AW_R40_GIC_SPI_UART3, + AW_R40_GIC_SPI_UART4, + AW_R40_GIC_SPI_UART5, + AW_R40_GIC_SPI_UART6, + AW_R40_GIC_SPI_UART7, + }; + const hwaddr addr = s->memmap[AW_R40_DEV_UART0 + i]; + + serial_mm_init(get_system_memory(), addr, 2, + qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]), + 115200, serial_hd(i), DEVICE_NATIVE_ENDIAN); + } /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 3be9dc962b..959b5dc4e0 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -41,6 +41,13 @@ enum { AW_R40_DEV_CCU, AW_R40_DEV_PIT, AW_R40_DEV_UART0, + AW_R40_DEV_UART1, + AW_R40_DEV_UART2, + AW_R40_DEV_UART3, + AW_R40_DEV_UART4, + AW_R40_DEV_UART5, + AW_R40_DEV_UART6, + AW_R40_DEV_UART7, AW_R40_DEV_GIC_DIST, AW_R40_DEV_GIC_CPU, AW_R40_DEV_GIC_HYP, @@ -70,6 +77,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40) * which are currently emulated by the R40 SoC code. */ #define AW_R40_NUM_MMCS 4 +#define AW_R40_NUM_UARTS 8 struct AwR40State { /*< private >*/ From 44814e210acb589bfdab073b3bed3a6d9a306ca3 Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:32 +0100 Subject: [PATCH 143/412] hw: arm: allwinner-r40: Add i2c0 device TWI(i2c) is designed to be used as an interface between CPU host and the serial 2-Wire bus. It can support all standard 2-Wire transfer, can be operated in standard mode(100kbit/s) or fast-mode, supporting data rate up to 400kbit/s. Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank Signed-off-by: Peter Maydell --- hw/arm/allwinner-r40.c | 11 ++++++++++- include/hw/arm/allwinner-r40.h | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 537a90b23d..4bc582630c 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -52,6 +52,7 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_UART5] = 0x01c29400, [AW_R40_DEV_UART6] = 0x01c29800, [AW_R40_DEV_UART7] = 0x01c29c00, + [AW_R40_DEV_TWI0] = 0x01c2ac00, [AW_R40_DEV_GIC_DIST] = 0x01c81000, [AW_R40_DEV_GIC_CPU] = 0x01c82000, [AW_R40_DEV_GIC_HYP] = 0x01c84000, @@ -115,7 +116,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = { { "uart7", 0x01c29c00, 1 * KiB }, { "ps20", 0x01c2a000, 1 * KiB }, { "ps21", 0x01c2a400, 1 * KiB }, - { "twi0", 0x01c2ac00, 1 * KiB }, { "twi1", 0x01c2b000, 1 * KiB }, { "twi2", 0x01c2b400, 1 * KiB }, { "twi3", 0x01c2b800, 1 * KiB }, @@ -167,6 +167,7 @@ enum { AW_R40_GIC_SPI_UART1 = 2, AW_R40_GIC_SPI_UART2 = 3, AW_R40_GIC_SPI_UART3 = 4, + AW_R40_GIC_SPI_TWI0 = 7, AW_R40_GIC_SPI_UART4 = 17, AW_R40_GIC_SPI_UART5 = 18, AW_R40_GIC_SPI_UART6 = 19, @@ -270,6 +271,8 @@ static void allwinner_r40_init(Object *obj) object_initialize_child(obj, mmc_names[i], &s->mmc[i], TYPE_AW_SDHOST_SUN5I); } + + object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); } static void allwinner_r40_realize(DeviceState *dev, Error **errp) @@ -416,6 +419,12 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) 115200, serial_hd(i), DEVICE_NATIVE_ENDIAN); } + /* I2C */ + sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_R40_DEV_TWI0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0)); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { create_unimplemented_device(r40_unimplemented[i].device_name, diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 959b5dc4e0..95366f4eee 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -26,6 +26,7 @@ #include "hw/intc/arm_gic.h" #include "hw/sd/allwinner-sdhost.h" #include "hw/misc/allwinner-r40-ccu.h" +#include "hw/i2c/allwinner-i2c.h" #include "target/arm/cpu.h" #include "sysemu/block-backend.h" @@ -48,6 +49,7 @@ enum { AW_R40_DEV_UART5, AW_R40_DEV_UART6, AW_R40_DEV_UART7, + AW_R40_DEV_TWI0, AW_R40_DEV_GIC_DIST, AW_R40_DEV_GIC_CPU, AW_R40_DEV_GIC_HYP, @@ -89,6 +91,7 @@ struct AwR40State { AwA10PITState timer; AwSdHostState mmc[AW_R40_NUM_MMCS]; AwR40ClockCtlState ccu; + AWI2CState i2c0; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; From a95454309269d579d936f3c9c736b436910f74f8 Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:32 +0100 Subject: [PATCH 144/412] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU This patch adds minimal support for AXP-221 PMU and connect it to bananapi M2U board. Signed-off-by: qianfan Zhao Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 3 +- hw/arm/bananapi_m2u.c | 6 + hw/misc/Kconfig | 2 +- hw/misc/axp209.c | 238 ----------------------------------- hw/misc/axp2xx.c | 283 ++++++++++++++++++++++++++++++++++++++++++ hw/misc/meson.build | 2 +- hw/misc/trace-events | 8 +- 7 files changed, 297 insertions(+), 245 deletions(-) delete mode 100644 hw/misc/axp209.c create mode 100644 hw/misc/axp2xx.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 02b2d8167d..007a81e6ed 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -383,7 +383,7 @@ config ALLWINNER_A10 select ALLWINNER_WDT select ALLWINNER_EMAC select ALLWINNER_I2C - select AXP209_PMU + select AXP2XX_PMU select SERIAL select UNIMP @@ -407,6 +407,7 @@ config ALLWINNER_R40 bool default y if TCG && ARM select ALLWINNER_A10_PIT + select AXP2XX_PMU select SERIAL select ARM_TIMER select ARM_GIC diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 1d49a006b5..9c5360a41b 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -23,6 +23,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/boards.h" +#include "hw/i2c/i2c.h" #include "hw/qdev-properties.h" #include "hw/arm/allwinner-r40.h" @@ -61,6 +62,7 @@ static void bpim2u_init(MachineState *machine) { bool bootroom_loaded = false; AwR40State *r40; + I2CBus *i2c; /* BIOS is not supported by this board */ if (machine->firmware) { @@ -104,6 +106,10 @@ static void bpim2u_init(MachineState *machine) } } + /* Connect AXP221 */ + i2c = I2C_BUS(qdev_get_child_bus(DEVICE(&r40->i2c0), "i2c")); + i2c_slave_create_simple(i2c, "axp221_pmu", 0x34); + /* SDRAM */ memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM], machine->ram); diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 2ef5781ef8..efeb430a6c 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -176,7 +176,7 @@ config ALLWINNER_A10_CCM config ALLWINNER_A10_DRAMC bool -config AXP209_PMU +config AXP2XX_PMU bool depends on I2C diff --git a/hw/misc/axp209.c b/hw/misc/axp209.c deleted file mode 100644 index 2908ed99a6..0000000000 --- a/hw/misc/axp209.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * AXP-209 PMU Emulation - * - * Copyright (C) 2022 Strahinja Jankovic - * - * 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. - * - * SPDX-License-Identifier: MIT - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "trace.h" -#include "hw/i2c/i2c.h" -#include "migration/vmstate.h" - -#define TYPE_AXP209_PMU "axp209_pmu" - -#define AXP209(obj) \ - OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP209_PMU) - -/* registers */ -enum { - REG_POWER_STATUS = 0x0u, - REG_OPERATING_MODE, - REG_OTG_VBUS_STATUS, - REG_CHIP_VERSION, - REG_DATA_CACHE_0, - REG_DATA_CACHE_1, - REG_DATA_CACHE_2, - REG_DATA_CACHE_3, - REG_DATA_CACHE_4, - REG_DATA_CACHE_5, - REG_DATA_CACHE_6, - REG_DATA_CACHE_7, - REG_DATA_CACHE_8, - REG_DATA_CACHE_9, - REG_DATA_CACHE_A, - REG_DATA_CACHE_B, - REG_POWER_OUTPUT_CTRL = 0x12u, - REG_DC_DC2_OUT_V_CTRL = 0x23u, - REG_DC_DC2_DVS_CTRL = 0x25u, - REG_DC_DC3_OUT_V_CTRL = 0x27u, - REG_LDO2_4_OUT_V_CTRL, - REG_LDO3_OUT_V_CTRL, - REG_VBUS_CH_MGMT = 0x30u, - REG_SHUTDOWN_V_CTRL, - REG_SHUTDOWN_CTRL, - REG_CHARGE_CTRL_1, - REG_CHARGE_CTRL_2, - REG_SPARE_CHARGE_CTRL, - REG_PEK_KEY_CTRL, - REG_DC_DC_FREQ_SET, - REG_CHR_TEMP_TH_SET, - REG_CHR_HIGH_TEMP_TH_CTRL, - REG_IPSOUT_WARN_L1, - REG_IPSOUT_WARN_L2, - REG_DISCHR_TEMP_TH_SET, - REG_DISCHR_HIGH_TEMP_TH_CTRL, - REG_IRQ_BANK_1_CTRL = 0x40u, - REG_IRQ_BANK_2_CTRL, - REG_IRQ_BANK_3_CTRL, - REG_IRQ_BANK_4_CTRL, - REG_IRQ_BANK_5_CTRL, - REG_IRQ_BANK_1_STAT = 0x48u, - REG_IRQ_BANK_2_STAT, - REG_IRQ_BANK_3_STAT, - REG_IRQ_BANK_4_STAT, - REG_IRQ_BANK_5_STAT, - REG_ADC_ACIN_V_H = 0x56u, - REG_ADC_ACIN_V_L, - REG_ADC_ACIN_CURR_H, - REG_ADC_ACIN_CURR_L, - REG_ADC_VBUS_V_H, - REG_ADC_VBUS_V_L, - REG_ADC_VBUS_CURR_H, - REG_ADC_VBUS_CURR_L, - REG_ADC_INT_TEMP_H, - REG_ADC_INT_TEMP_L, - REG_ADC_TEMP_SENS_V_H = 0x62u, - REG_ADC_TEMP_SENS_V_L, - REG_ADC_BAT_V_H = 0x78u, - REG_ADC_BAT_V_L, - REG_ADC_BAT_DISCHR_CURR_H, - REG_ADC_BAT_DISCHR_CURR_L, - REG_ADC_BAT_CHR_CURR_H, - REG_ADC_BAT_CHR_CURR_L, - REG_ADC_IPSOUT_V_H, - REG_ADC_IPSOUT_V_L, - REG_DC_DC_MOD_SEL = 0x80u, - REG_ADC_EN_1, - REG_ADC_EN_2, - REG_ADC_SR_CTRL, - REG_ADC_IN_RANGE, - REG_GPIO1_ADC_IRQ_RISING_TH, - REG_GPIO1_ADC_IRQ_FALLING_TH, - REG_TIMER_CTRL = 0x8au, - REG_VBUS_CTRL_MON_SRP, - REG_OVER_TEMP_SHUTDOWN = 0x8fu, - REG_GPIO0_FEAT_SET, - REG_GPIO_OUT_HIGH_SET, - REG_GPIO1_FEAT_SET, - REG_GPIO2_FEAT_SET, - REG_GPIO_SIG_STATE_SET_MON, - REG_GPIO3_SET, - REG_COULOMB_CNTR_CTRL = 0xb8u, - REG_POWER_MEAS_RES, - NR_REGS -}; - -#define AXP209_CHIP_VERSION_ID (0x01) -#define AXP209_DC_DC2_OUT_V_CTRL_RESET (0x16) -#define AXP209_IRQ_BANK_1_CTRL_RESET (0xd8) - -/* A simple I2C slave which returns values of ID or CNT register. */ -typedef struct AXP209I2CState { - /*< private >*/ - I2CSlave i2c; - /*< public >*/ - uint8_t regs[NR_REGS]; /* peripheral registers */ - uint8_t ptr; /* current register index */ - uint8_t count; /* counter used for tx/rx */ -} AXP209I2CState; - -/* Reset all counters and load ID register */ -static void axp209_reset_enter(Object *obj, ResetType type) -{ - AXP209I2CState *s = AXP209(obj); - - memset(s->regs, 0, NR_REGS); - s->ptr = 0; - s->count = 0; - s->regs[REG_CHIP_VERSION] = AXP209_CHIP_VERSION_ID; - s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP209_DC_DC2_OUT_V_CTRL_RESET; - s->regs[REG_IRQ_BANK_1_CTRL] = AXP209_IRQ_BANK_1_CTRL_RESET; -} - -/* Handle events from master. */ -static int axp209_event(I2CSlave *i2c, enum i2c_event event) -{ - AXP209I2CState *s = AXP209(i2c); - - s->count = 0; - - return 0; -} - -/* Called when master requests read */ -static uint8_t axp209_rx(I2CSlave *i2c) -{ - AXP209I2CState *s = AXP209(i2c); - uint8_t ret = 0xff; - - if (s->ptr < NR_REGS) { - ret = s->regs[s->ptr++]; - } - - trace_axp209_rx(s->ptr - 1, ret); - - return ret; -} - -/* - * Called when master sends write. - * Update ptr with byte 0, then perform write with second byte. - */ -static int axp209_tx(I2CSlave *i2c, uint8_t data) -{ - AXP209I2CState *s = AXP209(i2c); - - if (s->count == 0) { - /* Store register address */ - s->ptr = data; - s->count++; - trace_axp209_select(data); - } else { - trace_axp209_tx(s->ptr, data); - if (s->ptr == REG_DC_DC2_OUT_V_CTRL) { - s->regs[s->ptr++] = data; - } - } - - return 0; -} - -static const VMStateDescription vmstate_axp209 = { - .name = TYPE_AXP209_PMU, - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(regs, AXP209I2CState, NR_REGS), - VMSTATE_UINT8(count, AXP209I2CState), - VMSTATE_UINT8(ptr, AXP209I2CState), - VMSTATE_END_OF_LIST() - } -}; - -static void axp209_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); - ResettableClass *rc = RESETTABLE_CLASS(oc); - - rc->phases.enter = axp209_reset_enter; - dc->vmsd = &vmstate_axp209; - isc->event = axp209_event; - isc->recv = axp209_rx; - isc->send = axp209_tx; -} - -static const TypeInfo axp209_info = { - .name = TYPE_AXP209_PMU, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(AXP209I2CState), - .class_init = axp209_class_init -}; - -static void axp209_register_devices(void) -{ - type_register_static(&axp209_info); -} - -type_init(axp209_register_devices); diff --git a/hw/misc/axp2xx.c b/hw/misc/axp2xx.c new file mode 100644 index 0000000000..41538c1cd7 --- /dev/null +++ b/hw/misc/axp2xx.c @@ -0,0 +1,283 @@ +/* + * AXP-2XX PMU Emulation, supported lists: + * AXP209 + * AXP221 + * + * Copyright (C) 2022 Strahinja Jankovic + * Copyright (C) 2023 qianfan Zhao + * + * 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. + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qom/object.h" +#include "trace.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" + +#define TYPE_AXP2XX "axp2xx_pmu" +#define TYPE_AXP209_PMU "axp209_pmu" +#define TYPE_AXP221_PMU "axp221_pmu" + +OBJECT_DECLARE_TYPE(AXP2xxI2CState, AXP2xxClass, AXP2XX) + +#define NR_REGS (0xff) + +/* A simple I2C slave which returns values of ID or CNT register. */ +typedef struct AXP2xxI2CState { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + uint8_t regs[NR_REGS]; /* peripheral registers */ + uint8_t ptr; /* current register index */ + uint8_t count; /* counter used for tx/rx */ +} AXP2xxI2CState; + +typedef struct AXP2xxClass { + /*< private >*/ + I2CSlaveClass parent_class; + /*< public >*/ + void (*reset_enter)(AXP2xxI2CState *s, ResetType type); +} AXP2xxClass; + +#define AXP209_CHIP_VERSION_ID (0x01) +#define AXP209_DC_DC2_OUT_V_CTRL_RESET (0x16) + +/* Reset all counters and load ID register */ +static void axp209_reset_enter(AXP2xxI2CState *s, ResetType type) +{ + memset(s->regs, 0, NR_REGS); + s->ptr = 0; + s->count = 0; + + s->regs[0x03] = AXP209_CHIP_VERSION_ID; + s->regs[0x23] = AXP209_DC_DC2_OUT_V_CTRL_RESET; + + s->regs[0x30] = 0x60; + s->regs[0x32] = 0x46; + s->regs[0x34] = 0x41; + s->regs[0x35] = 0x22; + s->regs[0x36] = 0x5d; + s->regs[0x37] = 0x08; + s->regs[0x38] = 0xa5; + s->regs[0x39] = 0x1f; + s->regs[0x3a] = 0x68; + s->regs[0x3b] = 0x5f; + s->regs[0x3c] = 0xfc; + s->regs[0x3d] = 0x16; + s->regs[0x40] = 0xd8; + s->regs[0x42] = 0xff; + s->regs[0x43] = 0x3b; + s->regs[0x80] = 0xe0; + s->regs[0x82] = 0x83; + s->regs[0x83] = 0x80; + s->regs[0x84] = 0x32; + s->regs[0x86] = 0xff; + s->regs[0x90] = 0x07; + s->regs[0x91] = 0xa0; + s->regs[0x92] = 0x07; + s->regs[0x93] = 0x07; +} + +#define AXP221_PWR_STATUS_ACIN_PRESENT BIT(7) +#define AXP221_PWR_STATUS_ACIN_AVAIL BIT(6) +#define AXP221_PWR_STATUS_VBUS_PRESENT BIT(5) +#define AXP221_PWR_STATUS_VBUS_USED BIT(4) +#define AXP221_PWR_STATUS_BAT_CHARGING BIT(2) +#define AXP221_PWR_STATUS_ACIN_VBUS_POWERED BIT(1) + +/* Reset all counters and load ID register */ +static void axp221_reset_enter(AXP2xxI2CState *s, ResetType type) +{ + memset(s->regs, 0, NR_REGS); + s->ptr = 0; + s->count = 0; + + /* input power status register */ + s->regs[0x00] = AXP221_PWR_STATUS_ACIN_PRESENT + | AXP221_PWR_STATUS_ACIN_AVAIL + | AXP221_PWR_STATUS_ACIN_VBUS_POWERED; + + s->regs[0x01] = 0x00; /* no battery is connected */ + + /* + * CHIPID register, no documented on datasheet, but it is checked in + * u-boot spl. I had read it from AXP221s and got 0x06 value. + * So leave 06h here. + */ + s->regs[0x03] = 0x06; + + s->regs[0x10] = 0xbf; + s->regs[0x13] = 0x01; + s->regs[0x30] = 0x60; + s->regs[0x31] = 0x03; + s->regs[0x32] = 0x43; + s->regs[0x33] = 0xc6; + s->regs[0x34] = 0x45; + s->regs[0x35] = 0x0e; + s->regs[0x36] = 0x5d; + s->regs[0x37] = 0x08; + s->regs[0x38] = 0xa5; + s->regs[0x39] = 0x1f; + s->regs[0x3c] = 0xfc; + s->regs[0x3d] = 0x16; + s->regs[0x80] = 0x80; + s->regs[0x82] = 0xe0; + s->regs[0x84] = 0x32; + s->regs[0x8f] = 0x01; + + s->regs[0x90] = 0x07; + s->regs[0x91] = 0x1f; + s->regs[0x92] = 0x07; + s->regs[0x93] = 0x1f; + + s->regs[0x40] = 0xd8; + s->regs[0x41] = 0xff; + s->regs[0x42] = 0x03; + s->regs[0x43] = 0x03; + + s->regs[0xb8] = 0xc0; + s->regs[0xb9] = 0x64; + s->regs[0xe6] = 0xa0; +} + +static void axp2xx_reset_enter(Object *obj, ResetType type) +{ + AXP2xxI2CState *s = AXP2XX(obj); + AXP2xxClass *sc = AXP2XX_GET_CLASS(s); + + sc->reset_enter(s, type); +} + +/* Handle events from master. */ +static int axp2xx_event(I2CSlave *i2c, enum i2c_event event) +{ + AXP2xxI2CState *s = AXP2XX(i2c); + + s->count = 0; + + return 0; +} + +/* Called when master requests read */ +static uint8_t axp2xx_rx(I2CSlave *i2c) +{ + AXP2xxI2CState *s = AXP2XX(i2c); + uint8_t ret = 0xff; + + if (s->ptr < NR_REGS) { + ret = s->regs[s->ptr++]; + } + + trace_axp2xx_rx(s->ptr - 1, ret); + + return ret; +} + +/* + * Called when master sends write. + * Update ptr with byte 0, then perform write with second byte. + */ +static int axp2xx_tx(I2CSlave *i2c, uint8_t data) +{ + AXP2xxI2CState *s = AXP2XX(i2c); + + if (s->count == 0) { + /* Store register address */ + s->ptr = data; + s->count++; + trace_axp2xx_select(data); + } else { + trace_axp2xx_tx(s->ptr, data); + s->regs[s->ptr++] = data; + } + + return 0; +} + +static const VMStateDescription vmstate_axp2xx = { + .name = TYPE_AXP2XX, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS), + VMSTATE_UINT8(ptr, AXP2xxI2CState), + VMSTATE_UINT8(count, AXP2xxI2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void axp2xx_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + rc->phases.enter = axp2xx_reset_enter; + dc->vmsd = &vmstate_axp2xx; + isc->event = axp2xx_event; + isc->recv = axp2xx_rx; + isc->send = axp2xx_tx; +} + +static const TypeInfo axp2xx_info = { + .name = TYPE_AXP2XX, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(AXP2xxI2CState), + .class_size = sizeof(AXP2xxClass), + .class_init = axp2xx_class_init, + .abstract = true, +}; + +static void axp209_class_init(ObjectClass *oc, void *data) +{ + AXP2xxClass *sc = AXP2XX_CLASS(oc); + + sc->reset_enter = axp209_reset_enter; +} + +static const TypeInfo axp209_info = { + .name = TYPE_AXP209_PMU, + .parent = TYPE_AXP2XX, + .class_init = axp209_class_init +}; + +static void axp221_class_init(ObjectClass *oc, void *data) +{ + AXP2xxClass *sc = AXP2XX_CLASS(oc); + + sc->reset_enter = axp221_reset_enter; +} + +static const TypeInfo axp221_info = { + .name = TYPE_AXP221_PMU, + .parent = TYPE_AXP2XX, + .class_init = axp221_class_init, +}; + +static void axp2xx_register_devices(void) +{ + type_register_static(&axp2xx_info); + type_register_static(&axp209_info); + type_register_static(&axp221_info); +} + +type_init(axp2xx_register_devices); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 96e35f1cdb..1db0343333 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -45,7 +45,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c')) -softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c')) +softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) softmmu_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c47876a902..24cdec83fe 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -23,10 +23,10 @@ allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" avr_power_read(uint8_t value) "power_reduc read value:%u" avr_power_write(uint8_t value) "power_reduc write value:%u" -# axp209.c -axp209_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8 -axp209_select(uint8_t reg) "Accessing reg 0x%" PRIx8 -axp209_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8 +# axp2xx +axp2xx_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8 +axp2xx_select(uint8_t reg) "Accessing reg 0x%" PRIx8 +axp2xx_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8 # eccmemctl.c ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x" From 4a52ef61d901290da8ece2bf99546af1389ff7bb Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:32 +0100 Subject: [PATCH 145/412] hw/arm/allwinner-r40: add SDRAM controller device Types of memory that the SDRAM controller supports are DDR2/DDR3 and capacities of up to 2GiB. This commit adds emulation support of the Allwinner R40 SDRAM controller. This driver only support 256M, 512M and 1024M memory now. Signed-off-by: qianfan Zhao Signed-off-by: Peter Maydell --- hw/arm/allwinner-r40.c | 21 +- hw/arm/bananapi_m2u.c | 7 + hw/misc/allwinner-r40-dramc.c | 513 ++++++++++++++++++++++++++ hw/misc/meson.build | 1 + hw/misc/trace-events | 14 + include/hw/arm/allwinner-r40.h | 13 +- include/hw/misc/allwinner-r40-dramc.h | 108 ++++++ 7 files changed, 674 insertions(+), 3 deletions(-) create mode 100644 hw/misc/allwinner-r40-dramc.c create mode 100644 include/hw/misc/allwinner-r40-dramc.h diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 4bc582630c..0e4542d35f 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -31,6 +31,7 @@ #include "hw/loader.h" #include "sysemu/sysemu.h" #include "hw/arm/allwinner-r40.h" +#include "hw/misc/allwinner-r40-dramc.h" /* Memory map */ const hwaddr allwinner_r40_memmap[] = { @@ -53,6 +54,9 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_UART6] = 0x01c29800, [AW_R40_DEV_UART7] = 0x01c29c00, [AW_R40_DEV_TWI0] = 0x01c2ac00, + [AW_R40_DEV_DRAMCOM] = 0x01c62000, + [AW_R40_DEV_DRAMCTL] = 0x01c63000, + [AW_R40_DEV_DRAMPHY] = 0x01c65000, [AW_R40_DEV_GIC_DIST] = 0x01c81000, [AW_R40_DEV_GIC_CPU] = 0x01c82000, [AW_R40_DEV_GIC_HYP] = 0x01c84000, @@ -129,8 +133,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = { { "gpu", 0x01c40000, 64 * KiB }, { "gmac", 0x01c50000, 64 * KiB }, { "hstmr", 0x01c60000, 4 * KiB }, - { "dram-com", 0x01c62000, 4 * KiB }, - { "dram-ctl", 0x01c63000, 4 * KiB }, { "tcon-top", 0x01c70000, 4 * KiB }, { "lcd0", 0x01c71000, 4 * KiB }, { "lcd1", 0x01c72000, 4 * KiB }, @@ -273,6 +275,12 @@ static void allwinner_r40_init(Object *obj) } object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); + + object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_R40_DRAMC); + object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc), + "ram-addr"); + object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc), + "ram-size"); } static void allwinner_r40_realize(DeviceState *dev, Error **errp) @@ -425,6 +433,15 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0)); + /* DRAMC */ + sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, + s->memmap[AW_R40_DEV_DRAMCOM]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, + s->memmap[AW_R40_DEV_DRAMCTL]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, + s->memmap[AW_R40_DEV_DRAMPHY]); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { create_unimplemented_device(r40_unimplemented[i].device_name, diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 9c5360a41b..20a4550c68 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -85,6 +85,13 @@ static void bpim2u_init(MachineState *machine) object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000, &error_abort); + /* DRAMC */ + r40->ram_size = machine->ram_size / MiB; + object_property_set_uint(OBJECT(r40), "ram-addr", + r40->memmap[AW_R40_DEV_SDRAM], &error_abort); + object_property_set_int(OBJECT(r40), "ram-size", + r40->ram_size, &error_abort); + /* Mark R40 object realized */ qdev_realize(DEVICE(r40), NULL, &error_abort); diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c new file mode 100644 index 0000000000..ea6124744f --- /dev/null +++ b/hw/misc/allwinner-r40-dramc.c @@ -0,0 +1,513 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * CCopyright (C) 2023 qianfan Zhao + * + * 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/units.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/bitops.h" +#include "hw/misc/allwinner-r40-dramc.h" +#include "trace.h" + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* DRAMCOM register offsets */ +enum { + REG_DRAMCOM_CR = 0x0000, /* Control Register */ +}; + +/* DRAMCOMM register flags */ +enum { + REG_DRAMCOM_CR_DUAL_RANK = (1 << 0), +}; + +/* DRAMCTL register offsets */ +enum { + REG_DRAMCTL_PIR = 0x0000, /* PHY Initialization Register */ + REG_DRAMCTL_PGSR = 0x0010, /* PHY General Status Register */ + REG_DRAMCTL_STATR = 0x0018, /* Status Register */ + REG_DRAMCTL_PGCR = 0x0100, /* PHY general configuration registers */ +}; + +/* DRAMCTL register flags */ +enum { + REG_DRAMCTL_PGSR_INITDONE = (1 << 0), + REG_DRAMCTL_PGSR_READ_TIMEOUT = (1 << 13), + REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT = (1 << 25), +}; + +enum { + REG_DRAMCTL_STATR_ACTIVE = (1 << 0), +}; + +#define DRAM_MAX_ROW_BITS 16 +#define DRAM_MAX_COL_BITS 13 /* 8192 */ +#define DRAM_MAX_BANK 3 + +static uint64_t dram_autodetect_cells[DRAM_MAX_ROW_BITS] + [DRAM_MAX_BANK] + [DRAM_MAX_COL_BITS]; +struct VirtualDDRChip { + uint32_t ram_size; + uint8_t bank_bits; + uint8_t row_bits; + uint8_t col_bits; +}; + +/* + * Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported, + * 2GiB memory is not supported due to dual rank feature. + */ +static const struct VirtualDDRChip dummy_ddr_chips[] = { + { + .ram_size = 256, + .bank_bits = 3, + .row_bits = 12, + .col_bits = 13, + }, { + .ram_size = 512, + .bank_bits = 3, + .row_bits = 13, + .col_bits = 13, + }, { + .ram_size = 1024, + .bank_bits = 3, + .row_bits = 14, + .col_bits = 13, + }, { + 0 + } +}; + +static const struct VirtualDDRChip *get_match_ddr(uint32_t ram_size) +{ + const struct VirtualDDRChip *ddr; + + for (ddr = &dummy_ddr_chips[0]; ddr->ram_size; ddr++) { + if (ddr->ram_size == ram_size) { + return ddr; + } + } + + return NULL; +} + +static uint64_t *address_to_autodetect_cells(AwR40DramCtlState *s, + const struct VirtualDDRChip *ddr, + uint32_t offset) +{ + int row_index = 0, bank_index = 0, col_index = 0; + uint32_t row_addr, bank_addr, col_addr; + + row_addr = extract32(offset, s->set_col_bits + s->set_bank_bits, + s->set_row_bits); + bank_addr = extract32(offset, s->set_col_bits, s->set_bank_bits); + col_addr = extract32(offset, 0, s->set_col_bits); + + for (int i = 0; i < ddr->row_bits; i++) { + if (row_addr & BIT(i)) { + row_index = i; + } + } + + for (int i = 0; i < ddr->bank_bits; i++) { + if (bank_addr & BIT(i)) { + bank_index = i; + } + } + + for (int i = 0; i < ddr->col_bits; i++) { + if (col_addr & BIT(i)) { + col_index = i; + } + } + + trace_allwinner_r40_dramc_offset_to_cell(offset, row_index, bank_index, + col_index); + return &dram_autodetect_cells[row_index][bank_index][col_index]; +} + +static void allwinner_r40_dramc_map_rows(AwR40DramCtlState *s, uint8_t row_bits, + uint8_t bank_bits, uint8_t col_bits) +{ + const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size); + bool enable_detect_cells; + + trace_allwinner_r40_dramc_map_rows(row_bits, bank_bits, col_bits); + + if (!ddr) { + return; + } + + s->set_row_bits = row_bits; + s->set_bank_bits = bank_bits; + s->set_col_bits = col_bits; + + enable_detect_cells = ddr->bank_bits != bank_bits + || ddr->row_bits != row_bits + || ddr->col_bits != col_bits; + + if (enable_detect_cells) { + trace_allwinner_r40_dramc_detect_cells_enable(); + } else { + trace_allwinner_r40_dramc_detect_cells_disable(); + } + + memory_region_set_enabled(&s->detect_cells, enable_detect_cells); +} + +static uint64_t allwinner_r40_dramcom_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_R40_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_r40_dramcom_read(offset, s->dramcom[idx], size); + return s->dramcom[idx]; +} + +static void allwinner_r40_dramcom_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_r40_dramcom_write(offset, val, size); + + if (idx >= AW_R40_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCOM_CR: /* Control Register */ + if (!(val & REG_DRAMCOM_CR_DUAL_RANK)) { + allwinner_r40_dramc_map_rows(s, ((val >> 4) & 0xf) + 1, + ((val >> 2) & 0x1) + 2, + (((val >> 8) & 0xf) + 3)); + } + break; + }; + + s->dramcom[idx] = (uint32_t) val; +} + +static uint64_t allwinner_r40_dramctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_R40_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_r40_dramctl_read(offset, s->dramctl[idx], size); + return s->dramctl[idx]; +} + +static void allwinner_r40_dramctl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_r40_dramctl_write(offset, val, size); + + if (idx >= AW_R40_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCTL_PIR: /* PHY Initialization Register */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_INITDONE; + s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |= REG_DRAMCTL_STATR_ACTIVE; + break; + } + + s->dramctl[idx] = (uint32_t) val; +} + +static uint64_t allwinner_r40_dramphy_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_R40_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_r40_dramphy_read(offset, s->dramphy[idx], size); + return s->dramphy[idx]; +} + +static void allwinner_r40_dramphy_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_r40_dramphy_write(offset, val, size); + + if (idx >= AW_R40_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + s->dramphy[idx] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_r40_dramcom_ops = { + .read = allwinner_r40_dramcom_read, + .write = allwinner_r40_dramcom_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_r40_dramctl_ops = { + .read = allwinner_r40_dramctl_read, + .write = allwinner_r40_dramctl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_r40_dramphy_ops = { + .read = allwinner_r40_dramphy_read, + .write = allwinner_r40_dramphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static uint64_t allwinner_r40_detect_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size); + uint64_t data = 0; + + if (ddr) { + data = *address_to_autodetect_cells(s, ddr, (uint32_t)offset); + } + + trace_allwinner_r40_dramc_detect_cell_read(offset, data); + return data; +} + +static void allwinner_r40_detect_write(void *opaque, hwaddr offset, + uint64_t data, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size); + + if (ddr) { + uint64_t *cell = address_to_autodetect_cells(s, ddr, (uint32_t)offset); + trace_allwinner_r40_dramc_detect_cell_write(offset, data); + *cell = data; + } +} + +static const MemoryRegionOps allwinner_r40_detect_ops = { + .read = allwinner_r40_detect_read, + .write = allwinner_r40_detect_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +/* + * mctl_r40_detect_rank_count in u-boot will write the high 1G of DDR + * to detect wether the board support dual_rank or not. Create a virtual memory + * if the board's ram_size less or equal than 1G, and set read time out flag of + * REG_DRAMCTL_PGSR when the user touch this high dram. + */ +static uint64_t allwinner_r40_dualrank_detect_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + uint32_t reg; + + reg = s->dramctl[REG_INDEX(REG_DRAMCTL_PGCR)]; + if (reg & REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT) { /* Enable read time out */ + /* + * this driver only support one rank, mark READ_TIMEOUT when try + * read the second rank. + */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] + |= REG_DRAMCTL_PGSR_READ_TIMEOUT; + } + + return 0; +} + +static const MemoryRegionOps allwinner_r40_dualrank_detect_ops = { + .read = allwinner_r40_dualrank_detect_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_r40_dramc_reset(DeviceState *dev) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(dev); + + /* Set default values for registers */ + memset(&s->dramcom, 0, sizeof(s->dramcom)); + memset(&s->dramctl, 0, sizeof(s->dramctl)); + memset(&s->dramphy, 0, sizeof(s->dramphy)); +} + +static void allwinner_r40_dramc_realize(DeviceState *dev, Error **errp) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(dev); + + if (!get_match_ddr(s->ram_size)) { + error_report("%s: ram-size %u MiB is not supported", + __func__, s->ram_size); + exit(1); + } + + /* detect_cells */ + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s), 3, s->ram_addr, 10); + memory_region_set_enabled(&s->detect_cells, false); + + /* + * We only support DRAM size up to 1G now, so prepare a high memory page + * after 1G for dualrank detect. index = 4 + */ + memory_region_init_io(&s->dram_high, OBJECT(s), + &allwinner_r40_dualrank_detect_ops, s, + "DRAMHIGH", KiB); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->dram_high); + sysbus_mmio_map(SYS_BUS_DEVICE(s), 4, s->ram_addr + GiB); +} + +static void allwinner_r40_dramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwR40DramCtlState *s = AW_R40_DRAMC(obj); + + /* DRAMCOM registers, index 0 */ + memory_region_init_io(&s->dramcom_iomem, OBJECT(s), + &allwinner_r40_dramcom_ops, s, + "DRAMCOM", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramcom_iomem); + + /* DRAMCTL registers, index 1 */ + memory_region_init_io(&s->dramctl_iomem, OBJECT(s), + &allwinner_r40_dramctl_ops, s, + "DRAMCTL", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramctl_iomem); + + /* DRAMPHY registers. index 2 */ + memory_region_init_io(&s->dramphy_iomem, OBJECT(s), + &allwinner_r40_dramphy_ops, s, + "DRAMPHY", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramphy_iomem); + + /* R40 support max 2G memory but we only support up to 1G now. index 3 */ + memory_region_init_io(&s->detect_cells, OBJECT(s), + &allwinner_r40_detect_ops, s, + "DRAMCELLS", 1 * GiB); + sysbus_init_mmio(sbd, &s->detect_cells); +} + +static Property allwinner_r40_dramc_properties[] = { + DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState, ram_addr, 0x0), + DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState, ram_size, 256), /* MiB */ + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_r40_dramc_vmstate = { + .name = "allwinner-r40-dramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(dramcom, AwR40DramCtlState, + AW_R40_DRAMCOM_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramctl, AwR40DramCtlState, + AW_R40_DRAMCTL_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramphy, AwR40DramCtlState, + AW_R40_DRAMPHY_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_r40_dramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_r40_dramc_reset; + dc->vmsd = &allwinner_r40_dramc_vmstate; + dc->realize = allwinner_r40_dramc_realize; + device_class_set_props(dc, allwinner_r40_dramc_properties); +} + +static const TypeInfo allwinner_r40_dramc_info = { + .name = TYPE_AW_R40_DRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_r40_dramc_init, + .instance_size = sizeof(AwR40DramCtlState), + .class_init = allwinner_r40_dramc_class_init, +}; + +static void allwinner_r40_dramc_register(void) +{ + type_register_static(&allwinner_r40_dramc_info); +} + +type_init(allwinner_r40_dramc_register) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 1db0343333..b04d43e05a 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -45,6 +45,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c')) +softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c')) softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 24cdec83fe..8b68f07765 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -15,6 +15,20 @@ allwinner_h3_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write allwinner_h3_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +# allwinner-r40-dramc.c +allwinner_r40_dramc_detect_cells_disable(void) "Disable detect cells" +allwinner_r40_dramc_detect_cells_enable(void) "Enable detect cells" +allwinner_r40_dramc_map_rows(uint8_t row_bits, uint8_t bank_bits, uint8_t col_bits) "DRAM layout: row_bits %d, bank_bits %d, col_bits %d" +allwinner_r40_dramc_offset_to_cell(uint64_t offset, int row, int bank, int col) "offset 0x%" PRIx64 " row %d bank %d col %d" +allwinner_r40_dramc_detect_cell_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 "" +allwinner_r40_dramc_detect_cell_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 "" +allwinner_r40_dramcom_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramcom_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramctl_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + # allwinner-sid.c allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 95366f4eee..8243e8903b 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -26,6 +26,7 @@ #include "hw/intc/arm_gic.h" #include "hw/sd/allwinner-sdhost.h" #include "hw/misc/allwinner-r40-ccu.h" +#include "hw/misc/allwinner-r40-dramc.h" #include "hw/i2c/allwinner-i2c.h" #include "target/arm/cpu.h" #include "sysemu/block-backend.h" @@ -54,7 +55,10 @@ enum { AW_R40_DEV_GIC_CPU, AW_R40_DEV_GIC_HYP, AW_R40_DEV_GIC_VCPU, - AW_R40_DEV_SDRAM + AW_R40_DEV_SDRAM, + AW_R40_DEV_DRAMCOM, + AW_R40_DEV_DRAMCTL, + AW_R40_DEV_DRAMPHY, }; #define AW_R40_NUM_CPUS (4) @@ -86,11 +90,18 @@ struct AwR40State { DeviceState parent_obj; /*< public >*/ + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + ARMCPU cpus[AW_R40_NUM_CPUS]; const hwaddr *memmap; AwA10PITState timer; AwSdHostState mmc[AW_R40_NUM_MMCS]; AwR40ClockCtlState ccu; + AwR40DramCtlState dramc; AWI2CState i2c0; GICState gic; MemoryRegion sram_a1; diff --git a/include/hw/misc/allwinner-r40-dramc.h b/include/hw/misc/allwinner-r40-dramc.h new file mode 100644 index 0000000000..6a1a3a7893 --- /dev/null +++ b/include/hw/misc/allwinner-r40-dramc.h @@ -0,0 +1,108 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_MISC_ALLWINNER_R40_DRAMC_H +#define HW_MISC_ALLWINNER_R40_DRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "exec/hwaddr.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by DRAMCOM module */ +#define AW_R40_DRAMCOM_REGS_MAXADDR (0x804) + +/** Total number of known DRAMCOM registers */ +#define AW_R40_DRAMCOM_REGS_NUM (AW_R40_DRAMCOM_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMCTL module */ +#define AW_R40_DRAMCTL_REGS_MAXADDR (0x88c) + +/** Total number of known DRAMCTL registers */ +#define AW_R40_DRAMCTL_REGS_NUM (AW_R40_DRAMCTL_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMPHY module */ +#define AW_R40_DRAMPHY_REGS_MAXADDR (0x4) + +/** Total number of known DRAMPHY registers */ +#define AW_R40_DRAMPHY_REGS_NUM (AW_R40_DRAMPHY_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** @} */ + +/** + * Object model + * @{ + */ + +#define TYPE_AW_R40_DRAMC "allwinner-r40-dramc" +OBJECT_DECLARE_SIMPLE_TYPE(AwR40DramCtlState, AW_R40_DRAMC) + +/** @} */ + +/** + * Allwinner R40 SDRAM Controller object instance state. + */ +struct AwR40DramCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + + uint8_t set_row_bits; + uint8_t set_bank_bits; + uint8_t set_col_bits; + + /** + * @name Memory Regions + * @{ + */ + MemoryRegion dramcom_iomem; /**< DRAMCOM module I/O registers */ + MemoryRegion dramctl_iomem; /**< DRAMCTL module I/O registers */ + MemoryRegion dramphy_iomem; /**< DRAMPHY module I/O registers */ + MemoryRegion dram_high; /**< The high 1G dram for dualrank detect */ + MemoryRegion detect_cells; /**< DRAM memory cells for auto detect */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t dramcom[AW_R40_DRAMCOM_REGS_NUM]; /**< DRAMCOM registers */ + uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< DRAMCTL registers */ + uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< DRAMPHY registers */ + + /** @} */ + +}; + +#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */ From 2c992b88ccfc28897e5523b847c3dc1a68be0e11 Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:33 +0100 Subject: [PATCH 146/412] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support A64's sd register was similar to H3, and it introduced a new register named SAMP_DL_REG location at 0x144. The dma descriptor buffer size of mmc2 is only 8K and the other mmc controllers has 64K. Also fix allwinner-r40's mmc controller type. Signed-off-by: qianfan Zhao Signed-off-by: Peter Maydell --- hw/arm/allwinner-r40.c | 2 +- hw/sd/allwinner-sdhost.c | 72 ++++++++++++++++++++++++++++++-- include/hw/sd/allwinner-sdhost.h | 9 ++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 0e4542d35f..b148c56449 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -271,7 +271,7 @@ static void allwinner_r40_init(Object *obj) for (int i = 0; i < AW_R40_NUM_MMCS; i++) { object_initialize_child(obj, mmc_names[i], &s->mmc[i], - TYPE_AW_SDHOST_SUN5I); + TYPE_AW_SDHOST_SUN50I_A64); } object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index 92a0f42708..286e009509 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -77,6 +77,7 @@ enum { REG_SD_DATA1_CRC = 0x12C, /* CRC Data 1 from card/eMMC */ REG_SD_DATA0_CRC = 0x130, /* CRC Data 0 from card/eMMC */ REG_SD_CRC_STA = 0x134, /* CRC status from card/eMMC during write */ + REG_SD_SAMP_DL = 0x144, /* Sample Delay Control (sun50i-a64) */ REG_SD_FIFO = 0x200, /* Read/Write FIFO */ }; @@ -158,6 +159,7 @@ enum { REG_SD_RES_CRC_RST = 0x0, REG_SD_DATA_CRC_RST = 0x0, REG_SD_CRC_STA_RST = 0x0, + REG_SD_SAMPLE_DL_RST = 0x00002000, REG_SD_FIFO_RST = 0x0, }; @@ -459,6 +461,7 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, { AwSdHostState *s = AW_SDHOST(opaque); AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); + bool out_of_bounds = false; uint32_t res = 0; switch (offset) { @@ -577,13 +580,24 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, case REG_SD_FIFO: /* Read/Write FIFO */ res = allwinner_sdhost_fifo_read(s); break; + case REG_SD_SAMP_DL: /* Sample Delay */ + if (sc->can_calibrate) { + res = s->sample_delay; + } else { + out_of_bounds = true; + } + break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" - HWADDR_PRIx"\n", __func__, offset); + out_of_bounds = true; res = 0; break; } + if (out_of_bounds) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" + HWADDR_PRIx"\n", __func__, offset); + } + trace_allwinner_sdhost_read(offset, res, size); return res; } @@ -602,6 +616,7 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, { AwSdHostState *s = AW_SDHOST(opaque); AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); + bool out_of_bounds = false; trace_allwinner_sdhost_write(offset, value, size); @@ -725,10 +740,21 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ break; + case REG_SD_SAMP_DL: /* Sample delay control */ + if (sc->can_calibrate) { + s->sample_delay = value; + } else { + out_of_bounds = true; + } + break; default: + out_of_bounds = true; + break; + } + + if (out_of_bounds) { qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" HWADDR_PRIx"\n", __func__, offset); - break; } } @@ -777,6 +803,7 @@ static const VMStateDescription vmstate_allwinner_sdhost = { VMSTATE_UINT32(response_crc, AwSdHostState), VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8), VMSTATE_UINT32(status_crc, AwSdHostState), + VMSTATE_UINT32(sample_delay, AwSdHostState), VMSTATE_END_OF_LIST() } }; @@ -815,6 +842,7 @@ static void allwinner_sdhost_realize(DeviceState *dev, Error **errp) static void allwinner_sdhost_reset(DeviceState *dev) { AwSdHostState *s = AW_SDHOST(dev); + AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); s->global_ctl = REG_SD_GCTL_RST; s->clock_ctl = REG_SD_CKCR_RST; @@ -855,6 +883,10 @@ static void allwinner_sdhost_reset(DeviceState *dev) } s->status_crc = REG_SD_CRC_STA_RST; + + if (sc->can_calibrate) { + s->sample_delay = REG_SD_SAMPLE_DL_RST; + } } static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) @@ -879,6 +911,7 @@ static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; sc->is_sun4i = true; + sc->can_calibrate = false; } static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) @@ -886,6 +919,25 @@ static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 64 * KiB; sc->is_sun4i = false; + sc->can_calibrate = false; +} + +static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, + void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 64 * KiB; + sc->is_sun4i = false; + sc->can_calibrate = true; +} + +static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass, + void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 8 * KiB; + sc->is_sun4i = false; + sc->can_calibrate = true; } static const TypeInfo allwinner_sdhost_info = { @@ -910,6 +962,18 @@ static const TypeInfo allwinner_sdhost_sun5i_info = { .class_init = allwinner_sdhost_sun5i_class_init, }; +static const TypeInfo allwinner_sdhost_sun50i_a64_info = { + .name = TYPE_AW_SDHOST_SUN50I_A64, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun50i_a64_class_init, +}; + +static const TypeInfo allwinner_sdhost_sun50i_a64_emmc_info = { + .name = TYPE_AW_SDHOST_SUN50I_A64_EMMC, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun50i_a64_emmc_class_init, +}; + static const TypeInfo allwinner_sdhost_bus_info = { .name = TYPE_AW_SDHOST_BUS, .parent = TYPE_SD_BUS, @@ -922,6 +986,8 @@ static void allwinner_sdhost_register_types(void) type_register_static(&allwinner_sdhost_info); type_register_static(&allwinner_sdhost_sun4i_info); type_register_static(&allwinner_sdhost_sun5i_info); + type_register_static(&allwinner_sdhost_sun50i_a64_info); + type_register_static(&allwinner_sdhost_sun50i_a64_emmc_info); type_register_static(&allwinner_sdhost_bus_info); } diff --git a/include/hw/sd/allwinner-sdhost.h b/include/hw/sd/allwinner-sdhost.h index 30c1e60404..1b951177dd 100644 --- a/include/hw/sd/allwinner-sdhost.h +++ b/include/hw/sd/allwinner-sdhost.h @@ -38,6 +38,12 @@ /** Allwinner sun5i family and newer (A13, H2+, H3, etc) */ #define TYPE_AW_SDHOST_SUN5I TYPE_AW_SDHOST "-sun5i" +/** Allwinner sun50i-a64 */ +#define TYPE_AW_SDHOST_SUN50I_A64 TYPE_AW_SDHOST "-sun50i-a64" + +/** Allwinner sun50i-a64 emmc */ +#define TYPE_AW_SDHOST_SUN50I_A64_EMMC TYPE_AW_SDHOST "-sun50i-a64-emmc" + /** @} */ /** @@ -110,6 +116,7 @@ struct AwSdHostState { uint32_t startbit_detect; /**< eMMC DDR Start Bit Detection Control */ uint32_t response_crc; /**< Response CRC */ uint32_t data_crc[8]; /**< Data CRC */ + uint32_t sample_delay; /**< Sample delay control */ uint32_t status_crc; /**< Status CRC */ /** @} */ @@ -132,6 +139,8 @@ struct AwSdHostClass { size_t max_desc_size; bool is_sun4i; + /** does the IP block support autocalibration? */ + bool can_calibrate; }; #endif /* HW_SD_ALLWINNER_SDHOST_H */ From 0de1b69315b1b386d96282fa0b407f568fc5ede9 Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:33 +0100 Subject: [PATCH 147/412] hw: arm: allwinner-r40: Add emac and gmac support R40 has two ethernet controllers named as emac and gmac. The emac is compatibled with A10, and the GMAC is compatibled with H3. Signed-off-by: qianfan Zhao Signed-off-by: Peter Maydell --- hw/arm/allwinner-r40.c | 50 ++++++++++++++++++++++++++++++++-- hw/arm/bananapi_m2u.c | 3 ++ include/hw/arm/allwinner-r40.h | 6 ++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index b148c56449..c018ad231a 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -39,6 +39,7 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_SRAM_A2] = 0x00004000, [AW_R40_DEV_SRAM_A3] = 0x00008000, [AW_R40_DEV_SRAM_A4] = 0x0000b400, + [AW_R40_DEV_EMAC] = 0x01c0b000, [AW_R40_DEV_MMC0] = 0x01c0f000, [AW_R40_DEV_MMC1] = 0x01c10000, [AW_R40_DEV_MMC2] = 0x01c11000, @@ -54,6 +55,7 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_UART6] = 0x01c29800, [AW_R40_DEV_UART7] = 0x01c29c00, [AW_R40_DEV_TWI0] = 0x01c2ac00, + [AW_R40_DEV_GMAC] = 0x01c50000, [AW_R40_DEV_DRAMCOM] = 0x01c62000, [AW_R40_DEV_DRAMCTL] = 0x01c63000, [AW_R40_DEV_DRAMPHY] = 0x01c65000, @@ -82,7 +84,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = { { "spi1", 0x01c06000, 4 * KiB }, { "cs0", 0x01c09000, 4 * KiB }, { "keymem", 0x01c0a000, 4 * KiB }, - { "emac", 0x01c0b000, 4 * KiB }, { "usb0-otg", 0x01c13000, 4 * KiB }, { "usb0-host", 0x01c14000, 4 * KiB }, { "crypto", 0x01c15000, 4 * KiB }, @@ -131,7 +132,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = { { "tvd2", 0x01c33000, 4 * KiB }, { "tvd3", 0x01c34000, 4 * KiB }, { "gpu", 0x01c40000, 64 * KiB }, - { "gmac", 0x01c50000, 64 * KiB }, { "hstmr", 0x01c60000, 4 * KiB }, { "tcon-top", 0x01c70000, 4 * KiB }, { "lcd0", 0x01c71000, 4 * KiB }, @@ -180,6 +180,8 @@ enum { AW_R40_GIC_SPI_MMC1 = 33, AW_R40_GIC_SPI_MMC2 = 34, AW_R40_GIC_SPI_MMC3 = 35, + AW_R40_GIC_SPI_EMAC = 55, + AW_R40_GIC_SPI_GMAC = 85, }; /* Allwinner R40 general constants */ @@ -276,6 +278,11 @@ static void allwinner_r40_init(Object *obj) object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); + object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC); + object_initialize_child(obj, "gmac", &s->gmac, TYPE_AW_SUN8I_EMAC); + object_property_add_alias(obj, "gmac-phy-addr", + OBJECT(&s->gmac), "phy-addr"); + object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_R40_DRAMC); object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc), "ram-addr"); @@ -285,6 +292,7 @@ static void allwinner_r40_init(Object *obj) static void allwinner_r40_realize(DeviceState *dev, Error **errp) { + const char *r40_nic_models[] = { "gmac", "emac", NULL }; AwR40State *s = AW_R40(dev); unsigned i; @@ -442,6 +450,44 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_R40_DEV_DRAMPHY]); + /* nic support gmac and emac */ + for (int i = 0; i < ARRAY_SIZE(r40_nic_models) - 1; i++) { + NICInfo *nic = &nd_table[i]; + + if (!nic->used) { + continue; + } + if (qemu_show_nic_models(nic->model, r40_nic_models)) { + exit(0); + } + + switch (qemu_find_nic_model(nic, r40_nic_models, r40_nic_models[0])) { + case 0: /* gmac */ + qdev_set_nic_properties(DEVICE(&s->gmac), nic); + break; + case 1: /* emac */ + qdev_set_nic_properties(DEVICE(&s->emac), nic); + break; + default: + exit(1); + break; + } + } + + /* GMAC */ + object_property_set_link(OBJECT(&s->gmac), "dma-memory", + OBJECT(get_system_memory()), &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->gmac), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gmac), 0, s->memmap[AW_R40_DEV_GMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gmac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_GMAC)); + + /* EMAC */ + sysbus_realize(SYS_BUS_DEVICE(&s->emac), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_R40_DEV_EMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_EMAC)); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { create_unimplemented_device(r40_unimplemented[i].device_name, diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 20a4550c68..74121d8966 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -92,6 +92,9 @@ static void bpim2u_init(MachineState *machine) object_property_set_int(OBJECT(r40), "ram-size", r40->ram_size, &error_abort); + /* GMAC PHY */ + object_property_set_uint(OBJECT(r40), "gmac-phy-addr", 1, &error_abort); + /* Mark R40 object realized */ qdev_realize(DEVICE(r40), NULL, &error_abort); diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 8243e8903b..5f2d08489e 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -28,6 +28,8 @@ #include "hw/misc/allwinner-r40-ccu.h" #include "hw/misc/allwinner-r40-dramc.h" #include "hw/i2c/allwinner-i2c.h" +#include "hw/net/allwinner_emac.h" +#include "hw/net/allwinner-sun8i-emac.h" #include "target/arm/cpu.h" #include "sysemu/block-backend.h" @@ -36,6 +38,7 @@ enum { AW_R40_DEV_SRAM_A2, AW_R40_DEV_SRAM_A3, AW_R40_DEV_SRAM_A4, + AW_R40_DEV_EMAC, AW_R40_DEV_MMC0, AW_R40_DEV_MMC1, AW_R40_DEV_MMC2, @@ -51,6 +54,7 @@ enum { AW_R40_DEV_UART6, AW_R40_DEV_UART7, AW_R40_DEV_TWI0, + AW_R40_DEV_GMAC, AW_R40_DEV_GIC_DIST, AW_R40_DEV_GIC_CPU, AW_R40_DEV_GIC_HYP, @@ -103,6 +107,8 @@ struct AwR40State { AwR40ClockCtlState ccu; AwR40DramCtlState dramc; AWI2CState i2c0; + AwEmacState emac; + AwSun8iEmacState gmac; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; From 05def917e113ef95ef712ffd96d614203f5e8397 Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:33 +0100 Subject: [PATCH 148/412] hw: arm: allwinner-sramc: Add SRAM Controller support for R40 Only a few important registers are added, especially the SRAM_VER register. Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/allwinner-r40.c | 7 +- hw/misc/Kconfig | 3 + hw/misc/allwinner-sramc.c | 184 ++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + hw/misc/trace-events | 4 + include/hw/arm/allwinner-r40.h | 3 + include/hw/misc/allwinner-sramc.h | 69 +++++++++++ 8 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 hw/misc/allwinner-sramc.c create mode 100644 include/hw/misc/allwinner-sramc.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 007a81e6ed..2159de3ce6 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -406,6 +406,7 @@ config ALLWINNER_H3 config ALLWINNER_R40 bool default y if TCG && ARM + select ALLWINNER_SRAMC select ALLWINNER_A10_PIT select AXP2XX_PMU select SERIAL diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index c018ad231a..7d29eb224f 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -39,6 +39,7 @@ const hwaddr allwinner_r40_memmap[] = { [AW_R40_DEV_SRAM_A2] = 0x00004000, [AW_R40_DEV_SRAM_A3] = 0x00008000, [AW_R40_DEV_SRAM_A4] = 0x0000b400, + [AW_R40_DEV_SRAMC] = 0x01c00000, [AW_R40_DEV_EMAC] = 0x01c0b000, [AW_R40_DEV_MMC0] = 0x01c0f000, [AW_R40_DEV_MMC1] = 0x01c10000, @@ -76,7 +77,6 @@ struct AwR40Unimplemented { static struct AwR40Unimplemented r40_unimplemented[] = { { "d-engine", 0x01000000, 4 * MiB }, { "d-inter", 0x01400000, 128 * KiB }, - { "sram-c", 0x01c00000, 4 * KiB }, { "dma", 0x01c02000, 4 * KiB }, { "nfdc", 0x01c03000, 4 * KiB }, { "ts", 0x01c04000, 4 * KiB }, @@ -288,6 +288,8 @@ static void allwinner_r40_init(Object *obj) "ram-addr"); object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc), "ram-size"); + + object_initialize_child(obj, "sramc", &s->sramc, TYPE_AW_SRAMC_SUN8I_R40); } static void allwinner_r40_realize(DeviceState *dev, Error **errp) @@ -382,6 +384,9 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) AW_R40_GIC_SPI_TIMER1)); /* SRAM */ + sysbus_realize(SYS_BUS_DEVICE(&s->sramc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sramc), 0, s->memmap[AW_R40_DEV_SRAMC]); + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", 16 * KiB, &error_abort); memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index efeb430a6c..e4c2149175 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -170,6 +170,9 @@ config VIRT_CTRL config LASI bool +config ALLWINNER_SRAMC + bool + config ALLWINNER_A10_CCM bool diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c new file mode 100644 index 0000000000..a8b731f8f2 --- /dev/null +++ b/hw/misc/allwinner-sramc.c @@ -0,0 +1,184 @@ +/* + * Allwinner R40 SRAM controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/misc/allwinner-sramc.h" +#include "trace.h" + +/* + * register offsets + * https://linux-sunxi.org/SRAM_Controller_Register_Guide + */ +enum { + REG_SRAM_CTL1_CFG = 0x04, /* SRAM Control register 1 */ + REG_SRAM_VER = 0x24, /* SRAM Version register */ + REG_SRAM_R40_SOFT_ENTRY_REG0 = 0xbc, +}; + +/* REG_SRAMC_VERSION bit defines */ +#define SRAM_VER_READ_ENABLE (1 << 15) +#define SRAM_VER_VERSION_SHIFT 16 +#define SRAM_VERSION_SUN8I_R40 0x1701 + +static uint64_t allwinner_sramc_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwSRAMCState *s = AW_SRAMC(opaque); + AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s); + uint64_t val = 0; + + switch (offset) { + case REG_SRAM_CTL1_CFG: + val = s->sram_ctl1; + break; + case REG_SRAM_VER: + /* bit15: lock bit, set this bit before reading this register */ + if (s->sram_ver & SRAM_VER_READ_ENABLE) { + val = SRAM_VER_READ_ENABLE | + (sc->sram_version_code << SRAM_VER_VERSION_SHIFT); + } + break; + case REG_SRAM_R40_SOFT_ENTRY_REG0: + val = s->sram_soft_entry_reg0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_sramc_read(offset, val); + + return val; +} + +static void allwinner_sramc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwSRAMCState *s = AW_SRAMC(opaque); + + trace_allwinner_sramc_write(offset, val); + + switch (offset) { + case REG_SRAM_CTL1_CFG: + s->sram_ctl1 = val; + break; + case REG_SRAM_VER: + /* Only the READ_ENABLE bit is writeable */ + s->sram_ver = val & SRAM_VER_READ_ENABLE; + break; + case REG_SRAM_R40_SOFT_ENTRY_REG0: + s->sram_soft_entry_reg0 = val; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } +} + +static const MemoryRegionOps allwinner_sramc_ops = { + .read = allwinner_sramc_read, + .write = allwinner_sramc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const VMStateDescription allwinner_sramc_vmstate = { + .name = "allwinner-sramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sram_ver, AwSRAMCState), + VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sramc_reset(DeviceState *dev) +{ + AwSRAMCState *s = AW_SRAMC(dev); + AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s); + + switch (sc->sram_version_code) { + case SRAM_VERSION_SUN8I_R40: + s->sram_ctl1 = 0x1300; + break; + } +} + +static void allwinner_sramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_sramc_reset; + dc->vmsd = &allwinner_sramc_vmstate; +} + +static void allwinner_sramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwSRAMCState *s = AW_SRAMC(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sramc_ops, s, + TYPE_AW_SRAMC, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const TypeInfo allwinner_sramc_info = { + .name = TYPE_AW_SRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_sramc_init, + .instance_size = sizeof(AwSRAMCState), + .class_init = allwinner_sramc_class_init, +}; + +static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data) +{ + AwSRAMCClass *sc = AW_SRAMC_CLASS(klass); + + sc->sram_version_code = SRAM_VERSION_SUN8I_R40; +} + +static const TypeInfo allwinner_r40_sramc_info = { + .name = TYPE_AW_SRAMC_SUN8I_R40, + .parent = TYPE_AW_SRAMC, + .class_init = allwinner_r40_sramc_class_init, +}; + +static void allwinner_sramc_register(void) +{ + type_register_static(&allwinner_sramc_info); + type_register_static(&allwinner_r40_sramc_info); +} + +type_init(allwinner_sramc_register) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index b04d43e05a..78ca857c9d 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -37,6 +37,7 @@ subdir('macio') softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) +softmmu_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 8b68f07765..4d1a0e17af 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -33,6 +33,10 @@ allwinner_r40_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "writ allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +# allwinner-sramc.c +allwinner_sramc_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +allwinner_sramc_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 + # avr_power.c avr_power_read(uint8_t value) "power_reduc read value:%u" avr_power_write(uint8_t value) "power_reduc write value:%u" diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 5f2d08489e..72710d3edc 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -27,6 +27,7 @@ #include "hw/sd/allwinner-sdhost.h" #include "hw/misc/allwinner-r40-ccu.h" #include "hw/misc/allwinner-r40-dramc.h" +#include "hw/misc/allwinner-sramc.h" #include "hw/i2c/allwinner-i2c.h" #include "hw/net/allwinner_emac.h" #include "hw/net/allwinner-sun8i-emac.h" @@ -38,6 +39,7 @@ enum { AW_R40_DEV_SRAM_A2, AW_R40_DEV_SRAM_A3, AW_R40_DEV_SRAM_A4, + AW_R40_DEV_SRAMC, AW_R40_DEV_EMAC, AW_R40_DEV_MMC0, AW_R40_DEV_MMC1, @@ -102,6 +104,7 @@ struct AwR40State { ARMCPU cpus[AW_R40_NUM_CPUS]; const hwaddr *memmap; + AwSRAMCState sramc; AwA10PITState timer; AwSdHostState mmc[AW_R40_NUM_MMCS]; AwR40ClockCtlState ccu; diff --git a/include/hw/misc/allwinner-sramc.h b/include/hw/misc/allwinner-sramc.h new file mode 100644 index 0000000000..66b01b8d04 --- /dev/null +++ b/include/hw/misc/allwinner-sramc.h @@ -0,0 +1,69 @@ +/* + * Allwinner SRAM controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_MISC_ALLWINNER_SRAMC_H +#define HW_MISC_ALLWINNER_SRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "qemu/uuid.h" + +/** + * Object model + * @{ + */ +#define TYPE_AW_SRAMC "allwinner-sramc" +#define TYPE_AW_SRAMC_SUN8I_R40 TYPE_AW_SRAMC "-sun8i-r40" +OBJECT_DECLARE_TYPE(AwSRAMCState, AwSRAMCClass, AW_SRAMC) + +/** @} */ + +/** + * Allwinner SRAMC object instance state + */ +struct AwSRAMCState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /* registers */ + uint32_t sram_ctl1; + uint32_t sram_ver; + uint32_t sram_soft_entry_reg0; +}; + +/** + * Allwinner SRAM Controller class-level struct. + * + * This struct is filled by each sunxi device specific code + * such that the generic code can use this struct to support + * all devices. + */ +struct AwSRAMCClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + uint32_t sram_version_code; +}; + +#endif /* HW_MISC_ALLWINNER_SRAMC_H */ From 6c4f229a2e0d6f882bae389ce0c5bdaea712ce0f Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:34 +0100 Subject: [PATCH 149/412] tests: avocado: boot_linux_console: Add test case for bpim2u Add test case for booting from initrd and sd card. Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank Tested-by: Niek Linnenbank Signed-off-by: Peter Maydell --- tests/avocado/boot_linux_console.py | 176 ++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index c0675809e6..6ed660611f 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -769,6 +769,182 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern( 'Give root password for system maintenance') + def test_arm_bpim2u(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:bpim2u + :avocado: tags=accel:tcg + """ + deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' + 'linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = ('/usr/lib/linux-image-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.extract_from_deb(deb_path, dtb_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart,mmio32,0x1c28000') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + def test_arm_bpim2u_initrd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=accel:tcg + :avocado: tags=machine:bpim2u + """ + deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' + 'linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = ('/usr/lib/linux-image-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz') + initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' + initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + def test_arm_bpim2u_gmac(self): + """ + :avocado: tags=arch:arm + :avocado: tags=accel:tcg + :avocado: tags=machine:bpim2u + :avocado: tags=device:sd + """ + self.require_netdev('user') + + deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' + 'linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = ('/usr/lib/linux-image-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') + rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a' + rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.lzma_uncompress(rootfs_path_xz, rootfs_path) + image_pow2ceil_expand(rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'root=/dev/mmcblk0 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-net', 'nic,model=gmac,netdev=host_gmac', + '-netdev', 'user,id=host_gmac', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + shell_ready = "/bin/sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'mmcblk0') + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', + 'eth0: Link is Up') + exec_command_and_wait_for_pattern(self, 'udhcpc eth0', + 'udhcpc: lease of 10.0.2.15 obtained') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_bpim2u_openwrt_22_03_3(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:bpim2u + :avocado: tags=device:sd + """ + + # This test download a 8.9 MiB compressed image and expand it + # to 127 MiB. + image_url = ('https://downloads.openwrt.org/releases/22.03.3/targets/' + 'sunxi/cortexa7/openwrt-22.03.3-sunxi-cortexa7-' + 'sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz') + image_hash = ('5b41b4e11423e562c6011640f9a7cd3b' + 'dd0a3d42b83430f7caa70a432e6cd82c') + image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + image_path = archive.extract(image_path_gz, self.workdir) + image_pow2ceil_expand(image_path) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'usbcore.nousb ' + 'noreboot') + + self.wait_for_console_pattern('U-Boot SPL') + + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + + self.wait_for_console_pattern( + 'Please press Enter to activate this console.') + + exec_command_and_wait_for_pattern(self, ' ', 'root@') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + def test_arm_orangepi(self): """ :avocado: tags=arch:arm From 8d7f954a7f05dc1e537378f14ce4da72c54dc43a Mon Sep 17 00:00:00 2001 From: qianfan Zhao Date: Tue, 6 Jun 2023 10:19:34 +0100 Subject: [PATCH 150/412] docs: system: arm: Introduce bananapi_m2u Add documents for Banana Pi M2U Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank [PMM: Minor format fixes to correct sphinx errors] Signed-off-by: Peter Maydell --- docs/system/arm/bananapi_m2u.rst | 139 +++++++++++++++++++++++++++++++ docs/system/target-arm.rst | 1 + 2 files changed, 140 insertions(+) create mode 100644 docs/system/arm/bananapi_m2u.rst diff --git a/docs/system/arm/bananapi_m2u.rst b/docs/system/arm/bananapi_m2u.rst new file mode 100644 index 0000000000..b09ba5c548 --- /dev/null +++ b/docs/system/arm/bananapi_m2u.rst @@ -0,0 +1,139 @@ +Banana Pi BPI-M2U (``bpim2u``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Banana Pi BPI-M2 Ultra is a quad-core mini single board computer built with +Allwinner A40i/R40/V40 SoC. It features 2GB of RAM and 8GB eMMC. It also +has onboard WiFi and BT. On the ports side, the BPI-M2 Ultra has 2 USB A +2.0 ports, 1 USB OTG port, 1 HDMI port, 1 audio jack, a DC power port, +and last but not least, a SATA port. + +Supported devices +""""""""""""""""" + +The Banana Pi M2U machine supports the following devices: + + * SMP (Quad Core Cortex-A7) + * Generic Interrupt Controller configuration + * SRAM mappings + * SDRAM controller + * Timer device (re-used from Allwinner A10) + * UART + * SD/MMC storage controller + * EMAC ethernet + * GMAC ethernet + * Clock Control Unit + * TWI (I2C) + +Limitations +""""""""""" + +Currently, Banana Pi M2U does *not* support the following features: + +- Graphical output via HDMI, GPU and/or the Display Engine +- Audio output +- Hardware Watchdog +- Real Time Clock +- USB 2.0 interfaces + +Also see the 'unimplemented' array in the Allwinner R40 SoC module +for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-r40.c`` + +Boot options +"""""""""""" + +The Banana Pi M2U machine can start using the standard -kernel functionality +for loading a Linux kernel or ELF executable. Additionally, the Banana Pi M2U +machine can also emulate the BootROM which is present on an actual Allwinner R40 +based SoC, which loads the bootloader from a SD card, specified via the -sd +argument to qemu-system-arm. + +Running mainline Linux +"""""""""""""""""""""" + +To build a Linux mainline kernel that can be booted by the Banana Pi M2U machine, +simply configure the kernel using the sunxi_defconfig configuration: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig + +To boot the newly build linux kernel in QEMU with the Banana Pi M2U machine, use: + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dtb + +Banana Pi M2U images +"""""""""""""""""""" + +Note that the mainline kernel does not have a root filesystem. You can choose +to build you own image with buildroot using the bananapi_m2_ultra_defconfig. +Also see https://buildroot.org for more information. + +Another possibility is to run an OpenWrt image for Banana Pi M2U which +can be downloaded from: + + https://downloads.openwrt.org/releases/22.03.3/targets/sunxi/cortexa7/ + +When using an image as an SD card, it must be resized to a power of two. This can be +done with the ``qemu-img`` command. It is recommended to only increase the image size +instead of shrinking it to a power of two, to avoid loss of data. For example, +to prepare a downloaded Armbian image, first extract it and then increase +its size to one gigabyte as follows: + +.. code-block:: bash + + $ qemu-img resize \ + openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img \ + 1G + +Instead of providing a custom Linux kernel via the -kernel command you may also +choose to let the Banana Pi M2U machine load the bootloader from SD card, just like +a real board would do using the BootROM. Simply pass the selected image via the -sd +argument and remove the -kernel, -append, -dbt and -initrd arguments: + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nic user -nographic \ + -sd openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img + +Running U-Boot +"""""""""""""" + +U-Boot mainline can be build and configured using the Bananapi_M2_Ultra_defconfig +using similar commands as describe above for Linux. Note that it is recommended +for development/testing to select the following configuration setting in U-Boot: + + Device Tree Control > Provider for DTB for DT Control > Embedded DTB + +The BootROM of allwinner R40 loading u-boot from the 8KiB offset of sdcard. +Let's create an bootable disk image: + +.. code-block:: bash + + $ dd if=/dev/zero of=sd.img bs=32M count=1 + $ dd if=u-boot-sunxi-with-spl.bin of=sd.img bs=1k seek=8 conv=notrunc + +And then boot it. + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nographic -sd sd.img + +Banana Pi M2U integration tests +""""""""""""""""""""""""""""""" + +The Banana Pi M2U machine has several integration tests included. +To run the whole set of tests, build QEMU from source and simply +provide the following command: + +.. code-block:: bash + + $ cd qemu-build-dir + $ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \ + --verbose --show=app,console run -t machine:bpim2u \ + ../tests/avocado/boot_linux_console.py diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 91ebc26c6d..a12b6bca05 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -83,6 +83,7 @@ undocumented; you can get a complete list by running arm/versatile arm/vexpress arm/aspeed + arm/bananapi_m2u.rst arm/sabrelite arm/digic arm/cubieboard From 0f08429c4689514f4752454b99d7bd4e23f1cb71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:34 +0100 Subject: [PATCH 151/412] target/arm: Add commentary for CPUARMState.exclusive_high Document the meaning of exclusive_high in a big-endian context, and why we can't change it now. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index d469a2637b..81c0df9c25 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -677,8 +677,16 @@ typedef struct CPUArchState { uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ } vfp; + uint64_t exclusive_addr; uint64_t exclusive_val; + /* + * Contains the 'val' for the second 64-bit register of LDXP, which comes + * from the higher address, not the high part of a complete 128-bit value. + * In some ways it might be more convenient to record the exclusive value + * as the low and high halves of a 128 bit data value, but the current + * semantics of these fields are baked into the migration format. + */ uint64_t exclusive_high; /* iwMMXt coprocessor state. */ From cf1cbf50e8b8281428d1bcd02df955d2f59eb9e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:34 +0100 Subject: [PATCH 152/412] target/arm: Add feature test for FEAT_LSE2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 81c0df9c25..c1db26b299 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3851,6 +3851,11 @@ static inline bool isar_feature_aa64_st(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; } +static inline bool isar_feature_aa64_lse2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, AT) != 0; +} + static inline bool isar_feature_aa64_fwb(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, FWB) != 0; From e452ca5af88fc49b3026c2de0f1e65fd18d1a656 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:35 +0100 Subject: [PATCH 153/412] target/arm: Introduce finalize_memop_{atom,pair} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let finalize_memop_atom be the new basic function, with finalize_memop and finalize_memop_pair testing FEAT_LSE2 to apply the appropriate atomicity. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 2 ++ target/arm/tcg/translate.c | 1 + target/arm/tcg/translate.h | 39 +++++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d9800337cf..1d34c5a703 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -14098,6 +14098,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, tcg_debug_assert(dc->tbid & 1); #endif + dc->lse2 = dc_isar_feature(aa64_lse2, dc); + /* Single step state. The code-generation logic here is: * SS_ACTIVE == 0: * generate code with no special handling for single-stepping (except diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index a68d3c7f6d..13c88ba1b9 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -9168,6 +9168,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); } + dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; dc->features = env->features; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 4d88197715..c1e57a52ca 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -90,6 +90,7 @@ typedef struct DisasContext { uint64_t features; /* CPU features bits */ bool aarch64; bool thumb; + bool lse2; /* Because unallocated encodings generate different exception syndrome * information from traps due to FP being disabled, we can't do a single * "is fp access disabled" check at a high level in the decode tree. @@ -557,12 +558,13 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) } /** - * finalize_memop: + * finalize_memop_atom: * @s: DisasContext * @opc: size+sign+align of the memory operation + * @atom: atomicity of the memory operation * - * Build the complete MemOp for a memory operation, including alignment - * and endianness. + * Build the complete MemOp for a memory operation, including alignment, + * endianness, and atomicity. * * If (op & MO_AMASK) then the operation already contains the required * alignment, e.g. for AccType_ATOMIC. Otherwise, this an optionally @@ -572,12 +574,39 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) * and this is applied here. Note that there is no way to indicate that * no alignment should ever be enforced; this must be handled manually. */ -static inline MemOp finalize_memop(DisasContext *s, MemOp opc) +static inline MemOp finalize_memop_atom(DisasContext *s, MemOp opc, MemOp atom) { if (s->align_mem && !(opc & MO_AMASK)) { opc |= MO_ALIGN; } - return opc | s->be_data; + return opc | atom | s->be_data; +} + +/** + * finalize_memop: + * @s: DisasContext + * @opc: size+sign+align of the memory operation + * + * Like finalize_memop_atom, but with default atomicity. + */ +static inline MemOp finalize_memop(DisasContext *s, MemOp opc) +{ + MemOp atom = s->lse2 ? MO_ATOM_WITHIN16 : MO_ATOM_IFALIGN; + return finalize_memop_atom(s, opc, atom); +} + +/** + * finalize_memop_pair: + * @s: DisasContext + * @opc: size+sign+align of the memory operation + * + * Like finalize_memop_atom, but with atomicity for a pair. + * C.f. Pseudocode for Mem[], operand ispair. + */ +static inline MemOp finalize_memop_pair(DisasContext *s, MemOp opc) +{ + MemOp atom = s->lse2 ? MO_ATOM_WITHIN16_PAIR : MO_ATOM_IFALIGN_PAIR; + return finalize_memop_atom(s, opc, atom); } /** From c74cc082a6d3f8fde7778d26f600967582741967 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:35 +0100 Subject: [PATCH 154/412] target/arm: Use tcg_gen_qemu_ld_i128 for LDXP While we don't require 16-byte atomicity here, using a single larger load simplifies the code, and makes it a closer match to STXP. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1d34c5a703..1fff74c73a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2386,14 +2386,14 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i64 addr, int size, bool is_pair) { int idx = get_mem_index(s); - MemOp memop = s->be_data; + MemOp memop; g_assert(size <= 3); if (is_pair) { g_assert(size >= 2); if (size == 2) { /* The pair must be single-copy atomic for the doubleword. */ - memop |= MO_64 | MO_ALIGN; + memop = finalize_memop(s, MO_64 | MO_ALIGN); tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, memop); if (s->be_data == MO_LE) { tcg_gen_extract_i64(cpu_reg(s, rt), cpu_exclusive_val, 0, 32); @@ -2403,21 +2403,30 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, tcg_gen_extract_i64(cpu_reg(s, rt2), cpu_exclusive_val, 0, 32); } } else { - /* The pair must be single-copy atomic for *each* doubleword, not - the entire quadword, however it must be quadword aligned. */ - memop |= MO_64; - tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, - memop | MO_ALIGN_16); + /* + * The pair must be single-copy atomic for *each* doubleword, not + * the entire quadword, however it must be quadword aligned. + * Expose the complete load to tcg, for ease of tlb lookup, + * but indicate that only 8-byte atomicity is required. + */ + TCGv_i128 t16 = tcg_temp_new_i128(); - TCGv_i64 addr2 = tcg_temp_new_i64(); - tcg_gen_addi_i64(addr2, addr, 8); - tcg_gen_qemu_ld_i64(cpu_exclusive_high, addr2, idx, memop); + memop = finalize_memop_atom(s, MO_128 | MO_ALIGN_16, + MO_ATOM_IFALIGN_PAIR); + tcg_gen_qemu_ld_i128(t16, addr, idx, memop); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(cpu_exclusive_val, + cpu_exclusive_high, t16); + } else { + tcg_gen_extr_i128_i64(cpu_exclusive_high, + cpu_exclusive_val, t16); + } tcg_gen_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); tcg_gen_mov_i64(cpu_reg(s, rt2), cpu_exclusive_high); } } else { - memop |= size | MO_ALIGN; + memop = finalize_memop(s, size | MO_ALIGN); tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, memop); tcg_gen_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); } From d450bd0157be43d273116c3e3617883c8a0ac3d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:35 +0100 Subject: [PATCH 155/412] target/arm: Use tcg_gen_qemu_{st, ld}_i128 for do_fp_{st, ld} While we don't require 16-byte atomicity here, using a single larger operation simplifies the code. Introduce finalize_memop_asimd for this. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 35 +++++++++++----------------------- target/arm/tcg/translate.h | 24 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1fff74c73a..3674fc1bc1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -911,26 +911,20 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) { /* This writes the bottom N bits of a 128 bit wide vector to memory */ TCGv_i64 tmplo = tcg_temp_new_i64(); - MemOp mop; + MemOp mop = finalize_memop_asimd(s, size); tcg_gen_ld_i64(tmplo, cpu_env, fp_reg_offset(s, srcidx, MO_64)); - if (size < 4) { - mop = finalize_memop(s, size); + if (size < MO_128) { tcg_gen_qemu_st_i64(tmplo, tcg_addr, get_mem_index(s), mop); } else { - bool be = s->be_data == MO_BE; - TCGv_i64 tcg_hiaddr = tcg_temp_new_i64(); TCGv_i64 tmphi = tcg_temp_new_i64(); + TCGv_i128 t16 = tcg_temp_new_i128(); tcg_gen_ld_i64(tmphi, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_concat_i64_i128(t16, tmplo, tmphi); - mop = s->be_data | MO_UQ; - tcg_gen_qemu_st_i64(be ? tmphi : tmplo, tcg_addr, get_mem_index(s), - mop | (s->align_mem ? MO_ALIGN_16 : 0)); - tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_st_i64(be ? tmplo : tmphi, tcg_hiaddr, - get_mem_index(s), mop); + tcg_gen_qemu_st_i128(t16, tcg_addr, get_mem_index(s), mop); } } @@ -942,24 +936,17 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) /* This always zero-extends and writes to a full 128 bit wide vector */ TCGv_i64 tmplo = tcg_temp_new_i64(); TCGv_i64 tmphi = NULL; - MemOp mop; + MemOp mop = finalize_memop_asimd(s, size); - if (size < 4) { - mop = finalize_memop(s, size); + if (size < MO_128) { tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), mop); } else { - bool be = s->be_data == MO_BE; - TCGv_i64 tcg_hiaddr; + TCGv_i128 t16 = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(t16, tcg_addr, get_mem_index(s), mop); tmphi = tcg_temp_new_i64(); - tcg_hiaddr = tcg_temp_new_i64(); - - mop = s->be_data | MO_UQ; - tcg_gen_qemu_ld_i64(be ? tmphi : tmplo, tcg_addr, get_mem_index(s), - mop | (s->align_mem ? MO_ALIGN_16 : 0)); - tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_ld_i64(be ? tmplo : tmphi, tcg_hiaddr, - get_mem_index(s), mop); + tcg_gen_extr_i128_i64(tmplo, tmphi, t16); } tcg_gen_st_i64(tmplo, cpu_env, fp_reg_offset(s, destidx, MO_64)); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index c1e57a52ca..3aa486a1ab 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -609,6 +609,30 @@ static inline MemOp finalize_memop_pair(DisasContext *s, MemOp opc) return finalize_memop_atom(s, opc, atom); } +/** + * finalize_memop_asimd: + * @s: DisasContext + * @opc: size+sign+align of the memory operation + * + * Like finalize_memop_atom, but with atomicity of AccessType_ASIMD. + */ +static inline MemOp finalize_memop_asimd(DisasContext *s, MemOp opc) +{ + /* + * In the pseudocode for Mem[], with AccessType_ASIMD, size == 16, + * if IsAligned(8), the first case provides separate atomicity for + * the pair of 64-bit accesses. If !IsAligned(8), the middle cases + * do not apply, and we're left with the final case of no atomicity. + * Thus MO_ATOM_IFALIGN_PAIR. + * + * For other sizes, normal LSE2 rules apply. + */ + if ((opc & MO_SIZE) == MO_128) { + return finalize_memop_atom(s, opc, MO_ATOM_IFALIGN_PAIR); + } + return finalize_memop(s, opc); +} + /** * asimd_imm_const: Expand an encoded SIMD constant value * From e6073d88cc1fb43b00be16f79d9d6b0f9d2276f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:35 +0100 Subject: [PATCH 156/412] target/arm: Use tcg_gen_qemu_st_i128 for STZG, STZ2G This fixes a bug in that these two insns should have been using atomic 16-byte stores, since MTE is ARMv8.5 and LSE2 is mandatory from ARMv8.4. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3674fc1bc1..35eac7729b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4058,15 +4058,18 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) if (is_zero) { TCGv_i64 clean_addr = clean_data_tbi(s, addr); - TCGv_i64 tcg_zero = tcg_constant_i64(0); + TCGv_i64 zero64 = tcg_constant_i64(0); + TCGv_i128 zero128 = tcg_temp_new_i128(); int mem_index = get_mem_index(s); - int i, n = (1 + is_pair) << LOG2_TAG_GRANULE; + MemOp mop = finalize_memop(s, MO_128 | MO_ALIGN); - tcg_gen_qemu_st_i64(tcg_zero, clean_addr, mem_index, - MO_UQ | MO_ALIGN_16); - for (i = 8; i < n; i += 8) { - tcg_gen_addi_i64(clean_addr, clean_addr, 8); - tcg_gen_qemu_st_i64(tcg_zero, clean_addr, mem_index, MO_UQ); + tcg_gen_concat_i64_i128(zero128, zero64, zero64); + + /* This is 1 or 2 atomic 16-byte operations. */ + tcg_gen_qemu_st_i128(zero128, clean_addr, mem_index, mop); + if (is_pair) { + tcg_gen_addi_i64(clean_addr, clean_addr, 16); + tcg_gen_qemu_st_i128(zero128, clean_addr, mem_index, mop); } } From e6dd5e782becfe6d51f3575c086f5bd7162421d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:36 +0100 Subject: [PATCH 157/412] target/arm: Use tcg_gen_qemu_{ld, st}_i128 in gen_sve_{ld, st}r Round len_align to 16 instead of 8, handling an odd 8-byte as part of the tail. Use MO_ATOM_NONE to indicate that all of these memory ops have only byte atomicity. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 95 +++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index d9d5810dde..57051a4729 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4167,11 +4167,12 @@ TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, int len, int rn, int imm) { - int len_align = QEMU_ALIGN_DOWN(len, 8); - int len_remain = len % 8; - int nparts = len / 8 + ctpop8(len_remain); + int len_align = QEMU_ALIGN_DOWN(len, 16); + int len_remain = len % 16; + int nparts = len / 16 + ctpop8(len_remain); int midx = get_mem_index(s); TCGv_i64 dirty_addr, clean_addr, t0, t1; + TCGv_i128 t16; dirty_addr = tcg_temp_new_i64(); tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm); @@ -4188,10 +4189,16 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, int i; t0 = tcg_temp_new_i64(); - for (i = 0; i < len_align; i += 8) { - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ); + t1 = tcg_temp_new_i64(); + t16 = tcg_temp_new_i128(); + + for (i = 0; i < len_align; i += 16) { + tcg_gen_qemu_ld_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_extr_i128_i64(t0, t1, t16); tcg_gen_st_i64(t0, base, vofs + i); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + tcg_gen_st_i64(t1, base, vofs + i + 8); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); } } else { TCGLabel *loop = gen_new_label(); @@ -4200,14 +4207,21 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, tcg_gen_movi_ptr(i, 0); gen_set_label(loop); - t0 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + t16 = tcg_temp_new_i128(); + tcg_gen_qemu_ld_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); tp = tcg_temp_new_ptr(); tcg_gen_add_ptr(tp, base, i); - tcg_gen_addi_ptr(i, i, 8); + tcg_gen_addi_ptr(i, i, 16); + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_extr_i128_i64(t0, t1, t16); + tcg_gen_st_i64(t0, tp, vofs); + tcg_gen_st_i64(t1, tp, vofs + 8); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); } @@ -4216,6 +4230,16 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, * Predicate register loads can be any multiple of 2. * Note that we still store the entire 64-bit unit into cpu_env. */ + if (len_remain >= 8) { + t0 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE); + tcg_gen_st_i64(t0, base, vofs + len_align); + len_remain -= 8; + len_align += 8; + if (len_remain) { + tcg_gen_addi_i64(clean_addr, clean_addr, 8); + } + } if (len_remain) { t0 = tcg_temp_new_i64(); switch (len_remain) { @@ -4223,14 +4247,14 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, case 4: case 8: tcg_gen_qemu_ld_i64(t0, clean_addr, midx, - MO_LE | ctz32(len_remain)); + MO_LE | ctz32(len_remain) | MO_ATOM_NONE); break; case 6: t1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUL); + tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUL | MO_ATOM_NONE); tcg_gen_addi_i64(clean_addr, clean_addr, 4); - tcg_gen_qemu_ld_i64(t1, clean_addr, midx, MO_LEUW); + tcg_gen_qemu_ld_i64(t1, clean_addr, midx, MO_LEUW | MO_ATOM_NONE); tcg_gen_deposit_i64(t0, t0, t1, 32, 32); break; @@ -4245,11 +4269,12 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, int len, int rn, int imm) { - int len_align = QEMU_ALIGN_DOWN(len, 8); - int len_remain = len % 8; - int nparts = len / 8 + ctpop8(len_remain); + int len_align = QEMU_ALIGN_DOWN(len, 16); + int len_remain = len % 16; + int nparts = len / 16 + ctpop8(len_remain); int midx = get_mem_index(s); - TCGv_i64 dirty_addr, clean_addr, t0; + TCGv_i64 dirty_addr, clean_addr, t0, t1; + TCGv_i128 t16; dirty_addr = tcg_temp_new_i64(); tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm); @@ -4267,10 +4292,15 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, int i; t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t16 = tcg_temp_new_i128(); for (i = 0; i < len_align; i += 8) { tcg_gen_ld_i64(t0, base, vofs + i); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + tcg_gen_ld_i64(t1, base, vofs + i + 8); + tcg_gen_concat_i64_i128(t16, t0, t1); + tcg_gen_qemu_st_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); } } else { TCGLabel *loop = gen_new_label(); @@ -4280,18 +4310,33 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, gen_set_label(loop); t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); tp = tcg_temp_new_ptr(); tcg_gen_add_ptr(tp, base, i); tcg_gen_ld_i64(t0, tp, vofs); - tcg_gen_addi_ptr(i, i, 8); + tcg_gen_ld_i64(t1, tp, vofs + 8); + tcg_gen_addi_ptr(i, i, 16); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + t16 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(t16, t0, t1); + + tcg_gen_qemu_st_i128(t16, clean_addr, midx, MO_LEUQ); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); } /* Predicate register stores can be any multiple of 2. */ + if (len_remain >= 8) { + t0 = tcg_temp_new_i64(); + tcg_gen_st_i64(t0, base, vofs + len_align); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE); + len_remain -= 8; + len_align += 8; + if (len_remain) { + tcg_gen_addi_i64(clean_addr, clean_addr, 8); + } + } if (len_remain) { t0 = tcg_temp_new_i64(); tcg_gen_ld_i64(t0, base, vofs + len_align); @@ -4301,14 +4346,14 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, case 4: case 8: tcg_gen_qemu_st_i64(t0, clean_addr, midx, - MO_LE | ctz32(len_remain)); + MO_LE | ctz32(len_remain) | MO_ATOM_NONE); break; case 6: - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUL); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUL | MO_ATOM_NONE); tcg_gen_addi_i64(clean_addr, clean_addr, 4); tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUW); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUW | MO_ATOM_NONE); break; default: From 5c13983e23de4095e2dfa8bc52333ef40ebe40db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:36 +0100 Subject: [PATCH 158/412] target/arm: Sink gen_mte_check1 into load/store_exclusive No need to duplicate this check across multiple call sites. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 44 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 35eac7729b..729947b11a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2369,11 +2369,16 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn) * races in multi-threaded linux-user and when MTTCG softmmu is * enabled. */ -static void gen_load_exclusive(DisasContext *s, int rt, int rt2, - TCGv_i64 addr, int size, bool is_pair) +static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, + int size, bool is_pair) { int idx = get_mem_index(s); MemOp memop; + TCGv_i64 dirty_addr, clean_addr; + + s->is_ldex = true; + dirty_addr = cpu_reg_sp(s, rn); + clean_addr = gen_mte_check1(s, dirty_addr, false, rn != 31, size); g_assert(size <= 3); if (is_pair) { @@ -2381,7 +2386,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, if (size == 2) { /* The pair must be single-copy atomic for the doubleword. */ memop = finalize_memop(s, MO_64 | MO_ALIGN); - tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, memop); + tcg_gen_qemu_ld_i64(cpu_exclusive_val, clean_addr, idx, memop); if (s->be_data == MO_LE) { tcg_gen_extract_i64(cpu_reg(s, rt), cpu_exclusive_val, 0, 32); tcg_gen_extract_i64(cpu_reg(s, rt2), cpu_exclusive_val, 32, 32); @@ -2400,7 +2405,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, memop = finalize_memop_atom(s, MO_128 | MO_ALIGN_16, MO_ATOM_IFALIGN_PAIR); - tcg_gen_qemu_ld_i128(t16, addr, idx, memop); + tcg_gen_qemu_ld_i128(t16, clean_addr, idx, memop); if (s->be_data == MO_LE) { tcg_gen_extr_i128_i64(cpu_exclusive_val, @@ -2414,14 +2419,14 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, } } else { memop = finalize_memop(s, size | MO_ALIGN); - tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, memop); + tcg_gen_qemu_ld_i64(cpu_exclusive_val, clean_addr, idx, memop); tcg_gen_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); } - tcg_gen_mov_i64(cpu_exclusive_addr, addr); + tcg_gen_mov_i64(cpu_exclusive_addr, clean_addr); } static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, - TCGv_i64 addr, int size, int is_pair) + int rn, int size, int is_pair) { /* if (env->exclusive_addr == addr && env->exclusive_val == [addr] * && (!is_pair || env->exclusive_high == [addr + datasize])) { @@ -2437,9 +2442,12 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, */ TCGLabel *fail_label = gen_new_label(); TCGLabel *done_label = gen_new_label(); - TCGv_i64 tmp; + TCGv_i64 tmp, dirty_addr, clean_addr; - tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label); + dirty_addr = cpu_reg_sp(s, rn); + clean_addr = gen_mte_check1(s, dirty_addr, true, rn != 31, size); + + tcg_gen_brcond_i64(TCG_COND_NE, clean_addr, cpu_exclusive_addr, fail_label); tmp = tcg_temp_new_i64(); if (is_pair) { @@ -2627,9 +2635,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, size); - gen_store_exclusive(s, rs, rt, rt2, clean_addr, size, false); + gen_store_exclusive(s, rs, rt, rt2, rn, size, false); return; case 0x4: /* LDXR */ @@ -2637,10 +2643,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (rn == 31) { gen_check_sp_alignment(s); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, size); - s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, clean_addr, size, false); + gen_load_exclusive(s, rt, rt2, rn, size, false); if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } @@ -2692,9 +2695,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, size); - gen_store_exclusive(s, rs, rt, rt2, clean_addr, size, true); + gen_store_exclusive(s, rs, rt, rt2, rn, size, true); return; } if (rt2 == 31 @@ -2711,10 +2712,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (rn == 31) { gen_check_sp_alignment(s); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, size); - s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, clean_addr, size, true); + gen_load_exclusive(s, rt, rt2, rn, size, true); if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } From 6f47e7c18972802c428a5e03eb52a8f0a7bebe5c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:36 +0100 Subject: [PATCH 159/412] target/arm: Load/store integer pair with one tcg operation This is required for LSE2, where the pair must be treated atomically if it does not cross a 16-byte boundary. But it simplifies the code to do this always. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 70 ++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 729947b11a..88183f9dca 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2942,26 +2942,66 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); TCGv_i64 tcg_rt2 = cpu_reg(s, rt2); + MemOp mop = size + 1; + + /* + * With LSE2, non-sign-extending pairs are treated atomically if + * aligned, and if unaligned one of the pair will be completely + * within a 16-byte block and that element will be atomic. + * Otherwise each element is separately atomic. + * In all cases, issue one operation with the correct atomicity. + * + * This treats sign-extending loads like zero-extending loads, + * since that reuses the most code below. + */ + if (s->align_mem) { + mop |= (size == 2 ? MO_ALIGN_4 : MO_ALIGN_8); + } + mop = finalize_memop_pair(s, mop); if (is_load) { - TCGv_i64 tmp = tcg_temp_new_i64(); + if (size == 2) { + int o2 = s->be_data == MO_LE ? 32 : 0; + int o1 = o2 ^ 32; - /* Do not modify tcg_rt before recognizing any exception - * from the second load. - */ - do_gpr_ld(s, tmp, clean_addr, size + is_signed * MO_SIGN, - false, false, 0, false, false); - tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); - do_gpr_ld(s, tcg_rt2, clean_addr, size + is_signed * MO_SIGN, - false, false, 0, false, false); + tcg_gen_qemu_ld_i64(tcg_rt, clean_addr, get_mem_index(s), mop); + if (is_signed) { + tcg_gen_sextract_i64(tcg_rt2, tcg_rt, o2, 32); + tcg_gen_sextract_i64(tcg_rt, tcg_rt, o1, 32); + } else { + tcg_gen_extract_i64(tcg_rt2, tcg_rt, o2, 32); + tcg_gen_extract_i64(tcg_rt, tcg_rt, o1, 32); + } + } else { + TCGv_i128 tmp = tcg_temp_new_i128(); - tcg_gen_mov_i64(tcg_rt, tmp); + tcg_gen_qemu_ld_i128(tmp, clean_addr, get_mem_index(s), mop); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(tcg_rt, tcg_rt2, tmp); + } else { + tcg_gen_extr_i128_i64(tcg_rt2, tcg_rt, tmp); + } + } } else { - do_gpr_st(s, tcg_rt, clean_addr, size, - false, 0, false, false); - tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); - do_gpr_st(s, tcg_rt2, clean_addr, size, - false, 0, false, false); + if (size == 2) { + TCGv_i64 tmp = tcg_temp_new_i64(); + + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat32_i64(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i64(tmp, clean_addr, get_mem_index(s), mop); + } else { + TCGv_i128 tmp = tcg_temp_new_i128(); + + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat_i64_i128(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); + } } } From a75b66f617ed3e8b880df80291c0efad1e087675 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:37 +0100 Subject: [PATCH 160/412] target/arm: Hoist finalize_memop out of do_gpr_{ld, st} We are going to need the complete memop beforehand, so let's not compute it twice. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 61 +++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 88183f9dca..d90e8661ca 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -838,7 +838,6 @@ static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source, unsigned int iss_srt, bool iss_sf, bool iss_ar) { - memop = finalize_memop(s, memop); tcg_gen_qemu_st_i64(source, tcg_addr, memidx, memop); if (iss_valid) { @@ -873,7 +872,6 @@ static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, bool iss_valid, unsigned int iss_srt, bool iss_sf, bool iss_ar) { - memop = finalize_memop(s, memop); tcg_gen_qemu_ld_i64(dest, tcg_addr, memidx, memop); if (extend && (memop & MO_SIGN)) { @@ -2625,6 +2623,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; int size = extract32(insn, 30, 2); TCGv_i64 clean_addr; + MemOp memop; switch (o2_L_o1_o0) { case 0x0: /* STXR */ @@ -2661,10 +2660,11 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) gen_check_sp_alignment(s); } tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + /* TODO: ARMv8.4-LSE SCTLR.nAA */ + memop = finalize_memop(s, size | MO_ALIGN); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, size); - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - do_gpr_st(s, cpu_reg(s, rt), clean_addr, size | MO_ALIGN, true, rt, + do_gpr_st(s, cpu_reg(s, rt), clean_addr, memop, true, rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); return; @@ -2679,10 +2679,11 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (rn == 31) { gen_check_sp_alignment(s); } + /* TODO: ARMv8.4-LSE SCTLR.nAA */ + memop = finalize_memop(s, size | MO_ALIGN); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, size); - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size | MO_ALIGN, false, true, + do_gpr_ld(s, cpu_reg(s, rt), clean_addr, memop, false, true, rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); return; @@ -2790,9 +2791,9 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) } else { /* Only unsigned 32bit loads target 32bit registers. */ bool iss_sf = opc != 0; + MemOp memop = finalize_memop(s, size + is_signed * MO_SIGN); - do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, - false, true, rt, iss_sf, false); + do_gpr_ld(s, tcg_rt, clean_addr, memop, false, true, rt, iss_sf, false); } } @@ -3046,7 +3047,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, bool post_index; bool writeback; int memidx; - + MemOp memop; TCGv_i64 clean_addr, dirty_addr; if (is_vector) { @@ -3073,7 +3074,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, return; } is_store = (opc == 0); - is_signed = extract32(opc, 1, 1); + is_signed = !is_store && extract32(opc, 1, 1); is_extended = (size < 3) && extract32(opc, 0, 1); } @@ -3107,6 +3108,8 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, } memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + memop = finalize_memop(s, size + is_signed * MO_SIGN); + clean_addr = gen_mte_check1_mmuidx(s, dirty_addr, is_store, writeback || rn != 31, size, is_unpriv, memidx); @@ -3122,10 +3125,10 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); if (is_store) { - do_gpr_st_memidx(s, tcg_rt, clean_addr, size, memidx, + do_gpr_st_memidx(s, tcg_rt, clean_addr, memop, memidx, iss_valid, rt, iss_sf, false); } else { - do_gpr_ld_memidx(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, + do_gpr_ld_memidx(s, tcg_rt, clean_addr, memop, is_extended, memidx, iss_valid, rt, iss_sf, false); } @@ -3174,8 +3177,8 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, bool is_signed = false; bool is_store = false; bool is_extended = false; - TCGv_i64 tcg_rm, clean_addr, dirty_addr; + MemOp memop; if (extract32(opt, 1, 1) == 0) { unallocated_encoding(s); @@ -3202,7 +3205,7 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, return; } is_store = (opc == 0); - is_signed = extract32(opc, 1, 1); + is_signed = !is_store && extract32(opc, 1, 1); is_extended = (size < 3) && extract32(opc, 0, 1); } @@ -3215,6 +3218,8 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, ext_and_shift_reg(tcg_rm, tcg_rm, opt, shift ? size : 0); tcg_gen_add_i64(dirty_addr, dirty_addr, tcg_rm); + + memop = finalize_memop(s, size + is_signed * MO_SIGN); clean_addr = gen_mte_check1(s, dirty_addr, is_store, true, size); if (is_vector) { @@ -3226,11 +3231,12 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); + if (is_store) { - do_gpr_st(s, tcg_rt, clean_addr, size, + do_gpr_st(s, tcg_rt, clean_addr, memop, true, rt, iss_sf, false); } else { - do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, + do_gpr_ld(s, tcg_rt, clean_addr, memop, is_extended, true, rt, iss_sf, false); } } @@ -3262,12 +3268,11 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, int rn = extract32(insn, 5, 5); unsigned int imm12 = extract32(insn, 10, 12); unsigned int offset; - TCGv_i64 clean_addr, dirty_addr; - bool is_store; bool is_signed = false; bool is_extended = false; + MemOp memop; if (is_vector) { size |= (opc & 2) << 1; @@ -3289,7 +3294,7 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, return; } is_store = (opc == 0); - is_signed = extract32(opc, 1, 1); + is_signed = !is_store && extract32(opc, 1, 1); is_extended = (size < 3) && extract32(opc, 0, 1); } @@ -3299,6 +3304,8 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, dirty_addr = read_cpu_reg_sp(s, rn, 1); offset = imm12 << size; tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + + memop = finalize_memop(s, size + is_signed * MO_SIGN); clean_addr = gen_mte_check1(s, dirty_addr, is_store, rn != 31, size); if (is_vector) { @@ -3311,10 +3318,9 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, TCGv_i64 tcg_rt = cpu_reg(s, rt); bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); if (is_store) { - do_gpr_st(s, tcg_rt, clean_addr, size, - true, rt, iss_sf, false); + do_gpr_st(s, tcg_rt, clean_addr, memop, true, rt, iss_sf, false); } else { - do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, + do_gpr_ld(s, tcg_rt, clean_addr, memop, is_extended, true, rt, iss_sf, false); } } @@ -3344,7 +3350,7 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, bool a = extract32(insn, 23, 1); TCGv_i64 tcg_rs, tcg_rt, clean_addr; AtomicThreeOpFn *fn = NULL; - MemOp mop = s->be_data | size | MO_ALIGN; + MemOp mop = finalize_memop(s, size | MO_ALIGN); if (is_vector || !dc_isar_feature(aa64_atomics, s)) { unallocated_encoding(s); @@ -3405,7 +3411,7 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, * full load-acquire (we only need "load-acquire processor consistent"), * but we choose to implement them as full LDAQ. */ - do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, false, + do_gpr_ld(s, cpu_reg(s, rt), clean_addr, mop, false, true, rt, disas_ldst_compute_iss_sf(size, false, 0), true); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); return; @@ -3451,6 +3457,7 @@ static void disas_ldst_pac(DisasContext *s, uint32_t insn, bool use_key_a = !extract32(insn, 23, 1); int offset; TCGv_i64 clean_addr, dirty_addr, tcg_rt; + MemOp memop; if (size != 3 || is_vector || !dc_isar_feature(aa64_pauth, s)) { unallocated_encoding(s); @@ -3477,12 +3484,14 @@ static void disas_ldst_pac(DisasContext *s, uint32_t insn, offset = sextract32(offset << size, 0, 10 + size); tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + memop = finalize_memop(s, size); + /* Note that "clean" and "dirty" here refer to TBI not PAC. */ clean_addr = gen_mte_check1(s, dirty_addr, false, is_wback || rn != 31, size); tcg_rt = cpu_reg(s, rt); - do_gpr_ld(s, tcg_rt, clean_addr, size, + do_gpr_ld(s, tcg_rt, clean_addr, memop, /* extend */ false, /* iss_valid */ !is_wback, /* iss_srt */ rt, /* iss_sf */ true, /* iss_ar */ false); @@ -3524,7 +3533,7 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) } /* TODO: ARMv8.4-LSE SCTLR.nAA */ - mop = size | MO_ALIGN; + mop = finalize_memop(s, size | MO_ALIGN); switch (opc) { case 0: /* STLURB */ From 03176bcd03621f44c5282f8c398c378ad12069ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:37 +0100 Subject: [PATCH 161/412] target/arm: Hoist finalize_memop out of do_fp_{ld, st} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to need the complete memop beforehand, so let's not compute it twice. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 43 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d90e8661ca..3e93f6e848 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -905,15 +905,14 @@ static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, /* * Store from FP register to memory */ -static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) +static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, MemOp mop) { /* This writes the bottom N bits of a 128 bit wide vector to memory */ TCGv_i64 tmplo = tcg_temp_new_i64(); - MemOp mop = finalize_memop_asimd(s, size); tcg_gen_ld_i64(tmplo, cpu_env, fp_reg_offset(s, srcidx, MO_64)); - if (size < MO_128) { + if ((mop & MO_SIZE) < MO_128) { tcg_gen_qemu_st_i64(tmplo, tcg_addr, get_mem_index(s), mop); } else { TCGv_i64 tmphi = tcg_temp_new_i64(); @@ -929,14 +928,13 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) /* * Load from memory to FP register */ -static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) +static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, MemOp mop) { /* This always zero-extends and writes to a full 128 bit wide vector */ TCGv_i64 tmplo = tcg_temp_new_i64(); TCGv_i64 tmphi = NULL; - MemOp mop = finalize_memop_asimd(s, size); - if (size < MO_128) { + if ((mop & MO_SIZE) < MO_128) { tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), mop); } else { TCGv_i128 t16 = tcg_temp_new_i128(); @@ -2763,6 +2761,7 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) bool is_signed = false; int size = 2; TCGv_i64 tcg_rt, clean_addr; + MemOp memop; if (is_vector) { if (opc == 3) { @@ -2773,6 +2772,7 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) if (!fp_access_check(s)) { return; } + memop = finalize_memop_asimd(s, size); } else { if (opc == 3) { /* PRFM (literal) : prefetch */ @@ -2780,19 +2780,19 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) } size = 2 + extract32(opc, 0, 1); is_signed = extract32(opc, 1, 1); + memop = finalize_memop(s, size + is_signed * MO_SIGN); } tcg_rt = cpu_reg(s, rt); clean_addr = tcg_temp_new_i64(); gen_pc_plus_diff(s, clean_addr, imm); + if (is_vector) { - do_fp_ld(s, rt, clean_addr, size); + do_fp_ld(s, rt, clean_addr, memop); } else { /* Only unsigned 32bit loads target 32bit registers. */ bool iss_sf = opc != 0; - MemOp memop = finalize_memop(s, size + is_signed * MO_SIGN); - do_gpr_ld(s, tcg_rt, clean_addr, memop, false, true, rt, iss_sf, false); } } @@ -2929,16 +2929,18 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) (wback || rn != 31) && !set_tag, 2 << size); if (is_vector) { + MemOp mop = finalize_memop_asimd(s, size); + if (is_load) { - do_fp_ld(s, rt, clean_addr, size); + do_fp_ld(s, rt, clean_addr, mop); } else { - do_fp_st(s, rt, clean_addr, size); + do_fp_st(s, rt, clean_addr, mop); } tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); if (is_load) { - do_fp_ld(s, rt2, clean_addr, size); + do_fp_ld(s, rt2, clean_addr, mop); } else { - do_fp_st(s, rt2, clean_addr, size); + do_fp_st(s, rt2, clean_addr, mop); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); @@ -3060,6 +3062,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, if (!fp_access_check(s)) { return; } + memop = finalize_memop_asimd(s, size); } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ @@ -3076,6 +3079,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, is_store = (opc == 0); is_signed = !is_store && extract32(opc, 1, 1); is_extended = (size < 3) && extract32(opc, 0, 1); + memop = finalize_memop(s, size + is_signed * MO_SIGN); } switch (idx) { @@ -3108,7 +3112,6 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, } memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); - memop = finalize_memop(s, size + is_signed * MO_SIGN); clean_addr = gen_mte_check1_mmuidx(s, dirty_addr, is_store, writeback || rn != 31, @@ -3116,9 +3119,9 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, if (is_vector) { if (is_store) { - do_fp_st(s, rt, clean_addr, size); + do_fp_st(s, rt, clean_addr, memop); } else { - do_fp_ld(s, rt, clean_addr, size); + do_fp_ld(s, rt, clean_addr, memop); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); @@ -3224,9 +3227,9 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, if (is_vector) { if (is_store) { - do_fp_st(s, rt, clean_addr, size); + do_fp_st(s, rt, clean_addr, memop); } else { - do_fp_ld(s, rt, clean_addr, size); + do_fp_ld(s, rt, clean_addr, memop); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); @@ -3310,9 +3313,9 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, if (is_vector) { if (is_store) { - do_fp_st(s, rt, clean_addr, size); + do_fp_st(s, rt, clean_addr, memop); } else { - do_fp_ld(s, rt, clean_addr, size); + do_fp_ld(s, rt, clean_addr, memop); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); From 0a9091424d71bcb4638daee6260bbeed498310c4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:37 +0100 Subject: [PATCH 162/412] target/arm: Pass memop to gen_mte_check1* Pass the completed memop to gen_mte_check1_mmuidx. For the moment, do nothing more than extract the size. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 82 ++++++++++++++++++---------------- target/arm/tcg/translate-a64.h | 2 +- target/arm/tcg/translate-sve.c | 7 +-- 3 files changed, 49 insertions(+), 42 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3e93f6e848..85d896e975 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -253,7 +253,7 @@ static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, */ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, bool is_write, bool tag_checked, - int log2_size, bool is_unpriv, + MemOp memop, bool is_unpriv, int core_idx) { if (tag_checked && s->mte_active[is_unpriv]) { @@ -264,7 +264,7 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << log2_size) - 1); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, memop_size(memop) - 1); ret = tcg_temp_new_i64(); gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); @@ -275,9 +275,9 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, } TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int log2_size) + bool tag_checked, MemOp memop) { - return gen_mte_check1_mmuidx(s, addr, is_write, tag_checked, log2_size, + return gen_mte_check1_mmuidx(s, addr, is_write, tag_checked, memop, false, get_mem_index(s)); } @@ -2369,19 +2369,31 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, int size, bool is_pair) { int idx = get_mem_index(s); - MemOp memop; TCGv_i64 dirty_addr, clean_addr; + MemOp memop; + + /* + * For pairs: + * if size == 2, the operation is single-copy atomic for the doubleword. + * if size == 3, the operation is single-copy atomic for *each* doubleword, + * not the entire quadword, however it must be quadword aligned. + */ + memop = size + is_pair; + if (memop == MO_128) { + memop = finalize_memop_atom(s, MO_128 | MO_ALIGN, + MO_ATOM_IFALIGN_PAIR); + } else { + memop = finalize_memop(s, memop | MO_ALIGN); + } s->is_ldex = true; dirty_addr = cpu_reg_sp(s, rn); - clean_addr = gen_mte_check1(s, dirty_addr, false, rn != 31, size); + clean_addr = gen_mte_check1(s, dirty_addr, false, rn != 31, memop); g_assert(size <= 3); if (is_pair) { g_assert(size >= 2); if (size == 2) { - /* The pair must be single-copy atomic for the doubleword. */ - memop = finalize_memop(s, MO_64 | MO_ALIGN); tcg_gen_qemu_ld_i64(cpu_exclusive_val, clean_addr, idx, memop); if (s->be_data == MO_LE) { tcg_gen_extract_i64(cpu_reg(s, rt), cpu_exclusive_val, 0, 32); @@ -2391,16 +2403,8 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, tcg_gen_extract_i64(cpu_reg(s, rt2), cpu_exclusive_val, 0, 32); } } else { - /* - * The pair must be single-copy atomic for *each* doubleword, not - * the entire quadword, however it must be quadword aligned. - * Expose the complete load to tcg, for ease of tlb lookup, - * but indicate that only 8-byte atomicity is required. - */ TCGv_i128 t16 = tcg_temp_new_i128(); - memop = finalize_memop_atom(s, MO_128 | MO_ALIGN_16, - MO_ATOM_IFALIGN_PAIR); tcg_gen_qemu_ld_i128(t16, clean_addr, idx, memop); if (s->be_data == MO_LE) { @@ -2414,7 +2418,6 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, tcg_gen_mov_i64(cpu_reg(s, rt2), cpu_exclusive_high); } } else { - memop = finalize_memop(s, size | MO_ALIGN); tcg_gen_qemu_ld_i64(cpu_exclusive_val, clean_addr, idx, memop); tcg_gen_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); } @@ -2439,9 +2442,13 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGLabel *fail_label = gen_new_label(); TCGLabel *done_label = gen_new_label(); TCGv_i64 tmp, dirty_addr, clean_addr; + MemOp memop; + + memop = (size + is_pair) | MO_ALIGN; + memop = finalize_memop(s, memop); dirty_addr = cpu_reg_sp(s, rn); - clean_addr = gen_mte_check1(s, dirty_addr, true, rn != 31, size); + clean_addr = gen_mte_check1(s, dirty_addr, true, rn != 31, memop); tcg_gen_brcond_i64(TCG_COND_NE, clean_addr, cpu_exclusive_addr, fail_label); @@ -2455,8 +2462,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, cpu_exclusive_val, tmp, - get_mem_index(s), - MO_64 | MO_ALIGN | s->be_data); + get_mem_index(s), memop); tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); } else { TCGv_i128 t16 = tcg_temp_new_i128(); @@ -2474,8 +2480,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } tcg_gen_atomic_cmpxchg_i128(t16, cpu_exclusive_addr, c16, t16, - get_mem_index(s), - MO_128 | MO_ALIGN | s->be_data); + get_mem_index(s), memop); a = tcg_temp_new_i64(); b = tcg_temp_new_i64(); @@ -2493,8 +2498,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } } else { tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, cpu_exclusive_val, - cpu_reg(s, rt), get_mem_index(s), - size | MO_ALIGN | s->be_data); + cpu_reg(s, rt), get_mem_index(s), memop); tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); } tcg_gen_mov_i64(cpu_reg(s, rd), tmp); @@ -2513,13 +2517,15 @@ static void gen_compare_and_swap(DisasContext *s, int rs, int rt, TCGv_i64 tcg_rt = cpu_reg(s, rt); int memidx = get_mem_index(s); TCGv_i64 clean_addr; + MemOp memop; if (rn == 31) { gen_check_sp_alignment(s); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, size); - tcg_gen_atomic_cmpxchg_i64(tcg_rs, clean_addr, tcg_rs, tcg_rt, memidx, - size | MO_ALIGN | s->be_data); + memop = finalize_memop(s, size | MO_ALIGN); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); + tcg_gen_atomic_cmpxchg_i64(tcg_rs, clean_addr, tcg_rs, tcg_rt, + memidx, memop); } static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, @@ -2531,13 +2537,15 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, TCGv_i64 t2 = cpu_reg(s, rt + 1); TCGv_i64 clean_addr; int memidx = get_mem_index(s); + MemOp memop; if (rn == 31) { gen_check_sp_alignment(s); } /* This is a single atomic access, despite the "pair". */ - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, size + 1); + memop = finalize_memop(s, (size + 1) | MO_ALIGN); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); if (size == 2) { TCGv_i64 cmp = tcg_temp_new_i64(); @@ -2551,8 +2559,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, tcg_gen_concat32_i64(cmp, s2, s1); } - tcg_gen_atomic_cmpxchg_i64(cmp, clean_addr, cmp, val, memidx, - MO_64 | MO_ALIGN | s->be_data); + tcg_gen_atomic_cmpxchg_i64(cmp, clean_addr, cmp, val, memidx, memop); if (s->be_data == MO_LE) { tcg_gen_extr32_i64(s1, s2, cmp); @@ -2571,8 +2578,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, tcg_gen_concat_i64_i128(cmp, s2, s1); } - tcg_gen_atomic_cmpxchg_i128(cmp, clean_addr, cmp, val, memidx, - MO_128 | MO_ALIGN | s->be_data); + tcg_gen_atomic_cmpxchg_i128(cmp, clean_addr, cmp, val, memidx, memop); if (s->be_data == MO_LE) { tcg_gen_extr_i128_i64(s1, s2, cmp); @@ -2661,7 +2667,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) /* TODO: ARMv8.4-LSE SCTLR.nAA */ memop = finalize_memop(s, size | MO_ALIGN); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, size); + true, rn != 31, memop); do_gpr_st(s, cpu_reg(s, rt), clean_addr, memop, true, rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); return; @@ -2680,7 +2686,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) /* TODO: ARMv8.4-LSE SCTLR.nAA */ memop = finalize_memop(s, size | MO_ALIGN); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, size); + false, rn != 31, memop); do_gpr_ld(s, cpu_reg(s, rt), clean_addr, memop, false, true, rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); @@ -3223,7 +3229,7 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, tcg_gen_add_i64(dirty_addr, dirty_addr, tcg_rm); memop = finalize_memop(s, size + is_signed * MO_SIGN); - clean_addr = gen_mte_check1(s, dirty_addr, is_store, true, size); + clean_addr = gen_mte_check1(s, dirty_addr, is_store, true, memop); if (is_vector) { if (is_store) { @@ -3309,7 +3315,7 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); memop = finalize_memop(s, size + is_signed * MO_SIGN); - clean_addr = gen_mte_check1(s, dirty_addr, is_store, rn != 31, size); + clean_addr = gen_mte_check1(s, dirty_addr, is_store, rn != 31, memop); if (is_vector) { if (is_store) { @@ -3404,7 +3410,7 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, if (rn == 31) { gen_check_sp_alignment(s); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, size); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, mop); if (o3_opc == 014) { /* @@ -3491,7 +3497,7 @@ static void disas_ldst_pac(DisasContext *s, uint32_t insn, /* Note that "clean" and "dirty" here refer to TBI not PAC. */ clean_addr = gen_mte_check1(s, dirty_addr, false, - is_wback || rn != 31, size); + is_wback || rn != 31, memop); tcg_rt = cpu_reg(s, rt); do_gpr_ld(s, tcg_rt, clean_addr, memop, diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 0576c4ea12..cecf2bab8f 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -49,7 +49,7 @@ static inline bool sme_smza_enabled_check(DisasContext *s) TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr); TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int log2_size); + bool tag_checked, MemOp memop); TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, bool tag_checked, int size); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 57051a4729..671d2efa85 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -5009,6 +5009,7 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a) unsigned msz = dtype_msz(a->dtype); TCGLabel *over; TCGv_i64 temp, clean_addr; + MemOp memop; if (!dc_isar_feature(aa64_sve, s)) { return false; @@ -5038,10 +5039,10 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a) /* Load the data. */ temp = tcg_temp_new_i64(); tcg_gen_addi_i64(temp, cpu_reg_sp(s, a->rn), a->imm << msz); - clean_addr = gen_mte_check1(s, temp, false, true, msz); - tcg_gen_qemu_ld_i64(temp, clean_addr, get_mem_index(s), - finalize_memop(s, dtype_mop[a->dtype])); + memop = finalize_memop(s, dtype_mop[a->dtype]); + clean_addr = gen_mte_check1(s, temp, false, true, memop); + tcg_gen_qemu_ld_i64(temp, clean_addr, get_mem_index(s), memop); /* Broadcast to *all* elements. */ tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), From 3b97520c86e704b0533627c26b98173b71834bad Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:37 +0100 Subject: [PATCH 163/412] target/arm: Pass single_memop to gen_mte_checkN Pass the individual memop to gen_mte_checkN. For the moment, do nothing with it. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 31 +++++++++++++++++++------------ target/arm/tcg/translate-a64.h | 2 +- target/arm/tcg/translate-sve.c | 4 ++-- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 85d896e975..0b60069596 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -285,7 +285,7 @@ TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, * For MTE, check multiple logical sequential accesses. */ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int size) + bool tag_checked, int total_size, MemOp single_mop) { if (tag_checked && s->mte_active[0]) { TCGv_i64 ret; @@ -295,7 +295,7 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, size - 1); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, total_size - 1); ret = tcg_temp_new_i64(); gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); @@ -2841,14 +2841,12 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) bool is_vector = extract32(insn, 26, 1); bool is_load = extract32(insn, 22, 1); int opc = extract32(insn, 30, 2); - bool is_signed = false; bool postindex = false; bool wback = false; bool set_tag = false; - TCGv_i64 clean_addr, dirty_addr; - + MemOp mop; int size; if (opc == 3) { @@ -2931,12 +2929,17 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) } } + if (is_vector) { + mop = finalize_memop_asimd(s, size); + } else { + mop = finalize_memop(s, size); + } clean_addr = gen_mte_checkN(s, dirty_addr, !is_load, - (wback || rn != 31) && !set_tag, 2 << size); + (wback || rn != 31) && !set_tag, + 2 << size, mop); if (is_vector) { - MemOp mop = finalize_memop_asimd(s, size); - + /* LSE2 does not merge FP pairs; leave these as separate operations. */ if (is_load) { do_fp_ld(s, rt, clean_addr, mop); } else { @@ -2951,9 +2954,11 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); TCGv_i64 tcg_rt2 = cpu_reg(s, rt2); - MemOp mop = size + 1; /* + * We built mop above for the single logical access -- rebuild it + * now for the paired operation. + * * With LSE2, non-sign-extending pairs are treated atomically if * aligned, and if unaligned one of the pair will be completely * within a 16-byte block and that element will be atomic. @@ -2963,6 +2968,7 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) * This treats sign-extending loads like zero-extending loads, * since that reuses the most code below. */ + mop = size + 1; if (s->align_mem) { mop |= (size == 2 ? MO_ALIGN_4 : MO_ALIGN_8); } @@ -3741,7 +3747,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) * promote consecutive little-endian elements below. */ clean_addr = gen_mte_checkN(s, tcg_rn, is_store, is_postidx || rn != 31, - total); + total, finalize_memop(s, size)); /* * Consecutive little-endian elements from a single register @@ -3899,10 +3905,11 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) total = selem << scale; tcg_rn = cpu_reg_sp(s, rn); - clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31, - total); mop = finalize_memop(s, scale); + clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31, + total, mop); + tcg_ebytes = tcg_constant_i64(1 << scale); for (xs = 0; xs < selem; xs++) { if (replicate) { diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index cecf2bab8f..b55dc435fc 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -51,7 +51,7 @@ TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr); TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, bool tag_checked, MemOp memop); TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int size); + bool tag_checked, int total_size, MemOp memop); /* We should have at some point before trying to access an FP register * done the necessary access check, so assert that diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 671d2efa85..ff050626e6 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4176,7 +4176,7 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, dirty_addr = tcg_temp_new_i64(); tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm); - clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len); + clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len, MO_8); /* * Note that unpredicated load/store of vector/predicate registers @@ -4278,7 +4278,7 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, dirty_addr = tcg_temp_new_i64(); tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm); - clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len); + clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len, MO_8); /* Note that unpredicated load/store of vector/predicate registers * are defined as a stream of bytes, which equates to little-endian From 523da6b963455ce0a0e8d572d98d9cd91f952785 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:38 +0100 Subject: [PATCH 164/412] target/arm: Check alignment in helper_mte_check Fixes a bug in that with SCTLR.A set, we should raise any alignment fault before raising any MTE check fault. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 3 ++- target/arm/tcg/mte_helper.c | 18 ++++++++++++++++++ target/arm/tcg/translate-a64.c | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index ce26299f7f..e3029bdc37 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1242,7 +1242,8 @@ FIELD(MTEDESC, MIDX, 0, 4) FIELD(MTEDESC, TBI, 4, 2) FIELD(MTEDESC, TCMA, 6, 2) FIELD(MTEDESC, WRITE, 8, 1) -FIELD(MTEDESC, SIZEM1, 9, SIMD_DATA_BITS - 9) /* size - 1 */ +FIELD(MTEDESC, ALIGN, 9, 3) +FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - 12) /* size - 1 */ bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr); uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra); diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index a4f3f92bc0..9c64def081 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -785,6 +785,24 @@ uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra) uint64_t HELPER(mte_check)(CPUARMState *env, uint32_t desc, uint64_t ptr) { + /* + * R_XCHFJ: Alignment check not caused by memory type is priority 1, + * higher than any translation fault. When MTE is disabled, tcg + * performs the alignment check during the code generated for the + * memory access. With MTE enabled, we must check this here before + * raising any translation fault in allocation_tag_mem. + */ + unsigned align = FIELD_EX32(desc, MTEDESC, ALIGN); + if (unlikely(align)) { + align = (1u << align) - 1; + if (unlikely(ptr & align)) { + int idx = FIELD_EX32(desc, MTEDESC, MIDX); + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + MMUAccessType type = w ? MMU_DATA_STORE : MMU_DATA_LOAD; + arm_cpu_do_unaligned_access(env_cpu(env), ptr, type, idx, GETPC()); + } + } + return mte_check(env, desc, ptr, GETPC()); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0b60069596..77073a9c1d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -264,6 +264,7 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); + desc = FIELD_DP32(desc, MTEDESC, ALIGN, get_alignment_bits(memop)); desc = FIELD_DP32(desc, MTEDESC, SIZEM1, memop_size(memop) - 1); ret = tcg_temp_new_i64(); @@ -295,6 +296,7 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); + desc = FIELD_DP32(desc, MTEDESC, ALIGN, get_alignment_bits(single_mop)); desc = FIELD_DP32(desc, MTEDESC, SIZEM1, total_size - 1); ret = tcg_temp_new_i64(); From 83f624d9bae9f75b7004484e5c8adcb64ac2c6b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:38 +0100 Subject: [PATCH 165/412] target/arm: Add SCTLR.nAA to TBFLAG_A64 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 3 ++- target/arm/tcg/hflags.c | 6 ++++++ target/arm/tcg/translate-a64.c | 1 + target/arm/tcg/translate.h | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c1db26b299..36c608f0e6 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1248,7 +1248,7 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_D (1U << 5) /* up to v5; RAO in v6 */ #define SCTLR_CP15BEN (1U << 5) /* v7 onward */ #define SCTLR_L (1U << 6) /* up to v5; RAO in v6 and v7; RAZ in v8 */ -#define SCTLR_nAA (1U << 6) /* when v8.4-LSE is implemented */ +#define SCTLR_nAA (1U << 6) /* when FEAT_LSE2 is implemented */ #define SCTLR_B (1U << 7) /* up to v6; RAZ in v7 */ #define SCTLR_ITD (1U << 7) /* v8 onward */ #define SCTLR_S (1U << 8) /* up to v6; RAZ in v7 */ @@ -3044,6 +3044,7 @@ FIELD(TBFLAG_A64, SVL, 24, 4) /* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) FIELD(TBFLAG_A64, FGT_ERET, 29, 1) +FIELD(TBFLAG_A64, NAA, 30, 1) /* * Helpers for using the above. diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index b2ccd77cff..616c5fa723 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -248,6 +248,12 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } } + if (cpu_isar_feature(aa64_lse2, env_archcpu(env))) { + if (sctlr & SCTLR_nAA) { + DP_TBFLAG_A64(flags, NAA, 1); + } + } + /* Compute the condition for using AccType_UNPRIV for LDTR et al. */ if (!(env->pstate & PSTATE_UAO)) { switch (mmu_idx) { diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 77073a9c1d..91d28f8662 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -14151,6 +14151,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->pstate_sm = EX_TBFLAG_A64(tb_flags, PSTATE_SM); dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); + dc->naa = EX_TBFLAG_A64(tb_flags, NAA); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 3aa486a1ab..d1cacff0b2 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -142,6 +142,8 @@ typedef struct DisasContext { bool fgt_eret; /* True if fine-grained trap on SVC is enabled */ bool fgt_svc; + /* True if FEAT_LSE2 SCTLR_ELx.nAA is set */ + bool naa; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. From c1a1f80518d360b694b32d7a00cd91b79683d026 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:38 +0100 Subject: [PATCH 166/412] target/arm: Relax ordered/atomic alignment checks for LSE2 FEAT_LSE2 only requires that atomic operations not cross a 16-byte boundary. Ordered operations may be completely unaligned if SCTLR.nAA is set. Because this alignment check is so special, do it by hand. Make sure not to keep TCG temps live across the branch. Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-17-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 7 ++ target/arm/tcg/helper-a64.h | 3 + target/arm/tcg/translate-a64.c | 120 ++++++++++++++++++++++++++------- 3 files changed, 104 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index c3edf163be..1c9370f07b 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -952,3 +952,10 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) memset(mem, 0, blocklen); } + +void HELPER(unaligned_access)(CPUARMState *env, uint64_t addr, + uint32_t access_type, uint32_t mmu_idx) +{ + arm_cpu_do_unaligned_access(env_cpu(env), addr, access_type, + mmu_idx, GETPC()); +} diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index ff56807247..3d5957c11f 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -110,3 +110,6 @@ DEF_HELPER_FLAGS_2(st2g_stub, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(ldgm, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_3(stgm, TCG_CALL_NO_WG, void, env, i64, i64) DEF_HELPER_FLAGS_3(stzgm_tags, TCG_CALL_NO_WG, void, env, i64, i64) + +DEF_HELPER_FLAGS_4(unaligned_access, TCG_CALL_NO_WG, + noreturn, env, i64, i32, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 91d28f8662..adedebd1c2 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -307,6 +307,89 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, return clean_data_tbi(s, addr); } +/* + * Generate the special alignment check that applies to AccType_ATOMIC + * and AccType_ORDERED insns under FEAT_LSE2: the access need not be + * naturally aligned, but it must not cross a 16-byte boundary. + * See AArch64.CheckAlignment(). + */ +static void check_lse2_align(DisasContext *s, int rn, int imm, + bool is_write, MemOp mop) +{ + TCGv_i32 tmp; + TCGv_i64 addr; + TCGLabel *over_label; + MMUAccessType type; + int mmu_idx; + + tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, cpu_reg_sp(s, rn)); + tcg_gen_addi_i32(tmp, tmp, imm & 15); + tcg_gen_andi_i32(tmp, tmp, 15); + tcg_gen_addi_i32(tmp, tmp, memop_size(mop)); + + over_label = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_LEU, tmp, 16, over_label); + + addr = tcg_temp_new_i64(); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm); + + type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD, + mmu_idx = get_mem_index(s); + gen_helper_unaligned_access(cpu_env, addr, tcg_constant_i32(type), + tcg_constant_i32(mmu_idx)); + + gen_set_label(over_label); + +} + +/* Handle the alignment check for AccType_ATOMIC instructions. */ +static MemOp check_atomic_align(DisasContext *s, int rn, MemOp mop) +{ + MemOp size = mop & MO_SIZE; + + if (size == MO_8) { + return mop; + } + + /* + * If size == MO_128, this is a LDXP, and the operation is single-copy + * atomic for each doubleword, not the entire quadword; it still must + * be quadword aligned. + */ + if (size == MO_128) { + return finalize_memop_atom(s, MO_128 | MO_ALIGN, + MO_ATOM_IFALIGN_PAIR); + } + if (dc_isar_feature(aa64_lse2, s)) { + check_lse2_align(s, rn, 0, true, mop); + } else { + mop |= MO_ALIGN; + } + return finalize_memop(s, mop); +} + +/* Handle the alignment check for AccType_ORDERED instructions. */ +static MemOp check_ordered_align(DisasContext *s, int rn, int imm, + bool is_write, MemOp mop) +{ + MemOp size = mop & MO_SIZE; + + if (size == MO_8) { + return mop; + } + if (size == MO_128) { + return finalize_memop_atom(s, MO_128 | MO_ALIGN, + MO_ATOM_IFALIGN_PAIR); + } + if (!dc_isar_feature(aa64_lse2, s)) { + mop |= MO_ALIGN; + } else if (!s->naa) { + check_lse2_align(s, rn, imm, is_write, mop); + } + return finalize_memop(s, mop); +} + typedef struct DisasCompare64 { TCGCond cond; TCGv_i64 value; @@ -2372,21 +2455,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, { int idx = get_mem_index(s); TCGv_i64 dirty_addr, clean_addr; - MemOp memop; - - /* - * For pairs: - * if size == 2, the operation is single-copy atomic for the doubleword. - * if size == 3, the operation is single-copy atomic for *each* doubleword, - * not the entire quadword, however it must be quadword aligned. - */ - memop = size + is_pair; - if (memop == MO_128) { - memop = finalize_memop_atom(s, MO_128 | MO_ALIGN, - MO_ATOM_IFALIGN_PAIR); - } else { - memop = finalize_memop(s, memop | MO_ALIGN); - } + MemOp memop = check_atomic_align(s, rn, size + is_pair); s->is_ldex = true; dirty_addr = cpu_reg_sp(s, rn); @@ -2524,7 +2593,7 @@ static void gen_compare_and_swap(DisasContext *s, int rs, int rt, if (rn == 31) { gen_check_sp_alignment(s); } - memop = finalize_memop(s, size | MO_ALIGN); + memop = check_atomic_align(s, rn, size); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); tcg_gen_atomic_cmpxchg_i64(tcg_rs, clean_addr, tcg_rs, tcg_rt, memidx, memop); @@ -2546,7 +2615,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, } /* This is a single atomic access, despite the "pair". */ - memop = finalize_memop(s, (size + 1) | MO_ALIGN); + memop = check_atomic_align(s, rn, size + 1); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); if (size == 2) { @@ -2666,8 +2735,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) gen_check_sp_alignment(s); } tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - memop = finalize_memop(s, size | MO_ALIGN); + memop = check_ordered_align(s, rn, 0, true, size); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); do_gpr_st(s, cpu_reg(s, rt), clean_addr, memop, true, rt, @@ -2685,8 +2753,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (rn == 31) { gen_check_sp_alignment(s); } - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - memop = finalize_memop(s, size | MO_ALIGN); + memop = check_ordered_align(s, rn, 0, false, size); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, memop); do_gpr_ld(s, cpu_reg(s, rt), clean_addr, memop, false, true, @@ -3367,7 +3434,7 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, bool a = extract32(insn, 23, 1); TCGv_i64 tcg_rs, tcg_rt, clean_addr; AtomicThreeOpFn *fn = NULL; - MemOp mop = finalize_memop(s, size | MO_ALIGN); + MemOp mop = size; if (is_vector || !dc_isar_feature(aa64_atomics, s)) { unallocated_encoding(s); @@ -3418,6 +3485,8 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, if (rn == 31) { gen_check_sp_alignment(s); } + + mop = check_atomic_align(s, rn, mop); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, mop); if (o3_opc == 014) { @@ -3542,16 +3611,13 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) bool is_store = false; bool extend = false; bool iss_sf; - MemOp mop; + MemOp mop = size; if (!dc_isar_feature(aa64_rcpc_8_4, s)) { unallocated_encoding(s); return; } - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - mop = finalize_memop(s, size | MO_ALIGN); - switch (opc) { case 0: /* STLURB */ is_store = true; @@ -3583,6 +3649,8 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) gen_check_sp_alignment(s); } + mop = check_ordered_align(s, rn, offset, is_store, mop); + dirty_addr = read_cpu_reg_sp(s, rn, 1); tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); clean_addr = clean_data_tbi(s, dirty_addr); From 5096ec5b32f14906bbc16b0728066723f502fa71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:39 +0100 Subject: [PATCH 167/412] target/arm: Move mte check for store-exclusive Push the mte check behind the exclusive_addr check. Document the several ways that we are still out of spec with this implementation. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 42 +++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index adedebd1c2..aa93f37e21 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2512,17 +2512,47 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, */ TCGLabel *fail_label = gen_new_label(); TCGLabel *done_label = gen_new_label(); - TCGv_i64 tmp, dirty_addr, clean_addr; + TCGv_i64 tmp, clean_addr; MemOp memop; - memop = (size + is_pair) | MO_ALIGN; - memop = finalize_memop(s, memop); - - dirty_addr = cpu_reg_sp(s, rn); - clean_addr = gen_mte_check1(s, dirty_addr, true, rn != 31, memop); + /* + * FIXME: We are out of spec here. We have recorded only the address + * from load_exclusive, not the entire range, and we assume that the + * size of the access on both sides match. The architecture allows the + * store to be smaller than the load, so long as the stored bytes are + * within the range recorded by the load. + */ + /* See AArch64.ExclusiveMonitorsPass() and AArch64.IsExclusiveVA(). */ + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); tcg_gen_brcond_i64(TCG_COND_NE, clean_addr, cpu_exclusive_addr, fail_label); + /* + * The write, and any associated faults, only happen if the virtual + * and physical addresses pass the exclusive monitor check. These + * faults are exceedingly unlikely, because normally the guest uses + * the exact same address register for the load_exclusive, and we + * would have recognized these faults there. + * + * It is possible to trigger an alignment fault pre-LSE2, e.g. with an + * unaligned 4-byte write within the range of an aligned 8-byte load. + * With LSE2, the store would need to cross a 16-byte boundary when the + * load did not, which would mean the store is outside the range + * recorded for the monitor, which would have failed a corrected monitor + * check above. For now, we assume no size change and retain the + * MO_ALIGN to let tcg know what we checked in the load_exclusive. + * + * It is possible to trigger an MTE fault, by performing the load with + * a virtual address with a valid tag and performing the store with the + * same virtual address and a different invalid tag. + */ + memop = size + is_pair; + if (memop == MO_128 || !dc_isar_feature(aa64_lse2, s)) { + memop |= MO_ALIGN; + } + memop = finalize_memop(s, memop); + gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); + tmp = tcg_temp_new_i64(); if (is_pair) { if (size == 2) { From b7559ff7ce54c66ffdcdf1e41906298371b99109 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:39 +0100 Subject: [PATCH 168/412] tests/tcg/aarch64: Use stz2g in mte-7.c We have many other instances of stg in the testsuite; change these to provide an instance of stz2g. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/aarch64/mte-7.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/tcg/aarch64/mte-7.c b/tests/tcg/aarch64/mte-7.c index a981de62d4..04974f9ebb 100644 --- a/tests/tcg/aarch64/mte-7.c +++ b/tests/tcg/aarch64/mte-7.c @@ -19,8 +19,7 @@ int main(int ac, char **av) p = (void *)((unsigned long)p | (1ul << 56)); /* Store tag in sequential granules. */ - asm("stg %0, [%0]" : : "r"(p + 0x0ff0)); - asm("stg %0, [%0]" : : "r"(p + 0x1000)); + asm("stz2g %0, [%0]" : : "r"(p + 0x0ff0)); /* * Perform an unaligned store with tag 1 crossing the pages. From 6a6f4295748030e302a5768c5098a484d25a5b12 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:39 +0100 Subject: [PATCH 169/412] tests/tcg/multiarch: Adjust sigbus.c With -cpu max and FEAT_LSE2, the __aarch64__ section will only raise an alignment exception when the load crosses a 16-byte boundary. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/multiarch/sigbus.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/tcg/multiarch/sigbus.c b/tests/tcg/multiarch/sigbus.c index 8134c5fd56..f47c7390e7 100644 --- a/tests/tcg/multiarch/sigbus.c +++ b/tests/tcg/multiarch/sigbus.c @@ -6,8 +6,13 @@ #include -unsigned long long x = 0x8877665544332211ull; -void * volatile p = (void *)&x + 1; +char x[32] __attribute__((aligned(16))) = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, +}; +void * volatile p = (void *)&x + 15; void sigbus(int sig, siginfo_t *info, void *uc) { @@ -60,9 +65,9 @@ int main() * We might as well validate the unaligned load worked. */ if (BYTE_ORDER == LITTLE_ENDIAN) { - assert(tmp == 0x55443322); + assert(tmp == 0x13121110); } else { - assert(tmp == 0x77665544); + assert(tmp == 0x10111213); } return EXIT_SUCCESS; } From 59b6b42cd3446862567637f3a7ab31d69c9bef51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:19:39 +0100 Subject: [PATCH 170/412] target/arm: Enable FEAT_LSE2 for -cpu max Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20230530191438.411344-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 7338987875..ecbbd63adf 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -50,6 +50,7 @@ the following architecture extensions: - FEAT_LRCPC (Load-acquire RCpc instructions) - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) +- FEAT_LSE2 (Large System Extensions v2) - FEAT_LVA (Large Virtual Address space) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 886674a443..2976f94ae4 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -644,6 +644,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ + t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* FEAT_S2FWB */ t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* FEAT_TTL */ From cd4a47f907e6b4f91627fcdad5174ecb2570ad0e Mon Sep 17 00:00:00 2001 From: Zhuojia Shen Date: Tue, 6 Jun 2023 10:19:40 +0100 Subject: [PATCH 171/412] target/arm: allow DC CVA[D]P in user mode emulation DC CVAP and DC CVADP instructions can be executed in EL0 on Linux, either directly when SCTLR_EL1.UCI == 1 or emulated by the kernel (see user_cache_maint_handler() in arch/arm64/kernel/traps.c). This patch enables execution of the two instructions in user mode emulation. Signed-off-by: Zhuojia Shen Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0b7fd2e7e6..d4bee43bd0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7405,7 +7405,6 @@ static const ARMCPRegInfo rndr_reginfo[] = { .access = PL0_R, .readfn = rndr_readfn }, }; -#ifndef CONFIG_USER_ONLY static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, uint64_t value) { @@ -7420,6 +7419,7 @@ static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, /* This won't be crossing page boundaries */ haddr = probe_read(env, vaddr, dline_size, mem_idx, GETPC()); if (haddr) { +#ifndef CONFIG_USER_ONLY ram_addr_t offset; MemoryRegion *mr; @@ -7430,6 +7430,7 @@ static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, if (mr) { memory_region_writeback(mr, offset, dline_size); } +#endif /*CONFIG_USER_ONLY*/ } } @@ -7448,7 +7449,6 @@ static const ARMCPRegInfo dcpodp_reg[] = { .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, }; -#endif /*CONFIG_USER_ONLY*/ static CPAccessResult access_aa64_tid5(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -9092,7 +9092,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_tlbios, cpu)) { define_arm_cp_regs(cpu, tlbios_reginfo); } -#ifndef CONFIG_USER_ONLY /* Data Cache clean instructions up to PoP */ if (cpu_isar_feature(aa64_dcpop, cpu)) { define_one_arm_cp_reg(cpu, dcpop_reg); @@ -9101,7 +9100,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, dcpodp_reg); } } -#endif /*CONFIG_USER_ONLY*/ /* * If full MTE is enabled, add all of the system registers. From c81e4ab370e858550852b052f689a0a721b879d4 Mon Sep 17 00:00:00 2001 From: Zhuojia Shen Date: Tue, 6 Jun 2023 10:19:40 +0100 Subject: [PATCH 172/412] tests/tcg/aarch64: add DC CVA[D]P tests Test execution of DC CVAP and DC CVADP instructions under user mode emulation. Signed-off-by: Zhuojia Shen Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- tests/tcg/aarch64/Makefile.target | 11 ++++++ tests/tcg/aarch64/dcpodp.c | 63 +++++++++++++++++++++++++++++++ tests/tcg/aarch64/dcpop.c | 63 +++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 tests/tcg/aarch64/dcpodp.c create mode 100644 tests/tcg/aarch64/dcpop.c diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 0315795487..3430fd3cd8 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -21,12 +21,23 @@ config-cc.mak: Makefile $(quiet-@)( \ $(call cc-option,-march=armv8.1-a+sve, CROSS_CC_HAS_SVE); \ $(call cc-option,-march=armv8.1-a+sve2, CROSS_CC_HAS_SVE2); \ + $(call cc-option,-march=armv8.2-a, CROSS_CC_HAS_ARMV8_2); \ $(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3); \ + $(call cc-option,-march=armv8.5-a, CROSS_CC_HAS_ARMV8_5); \ $(call cc-option,-mbranch-protection=standard, CROSS_CC_HAS_ARMV8_BTI); \ $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE); \ $(call cc-option,-march=armv9-a+sme, CROSS_CC_HAS_ARMV9_SME)) 3> config-cc.mak -include config-cc.mak +ifneq ($(CROSS_CC_HAS_ARMV8_2),) +AARCH64_TESTS += dcpop +dcpop: CFLAGS += -march=armv8.2-a +endif +ifneq ($(CROSS_CC_HAS_ARMV8_5),) +AARCH64_TESTS += dcpodp +dcpodp: CFLAGS += -march=armv8.5-a +endif + # Pauth Tests ifneq ($(CROSS_CC_HAS_ARMV8_3),) AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 diff --git a/tests/tcg/aarch64/dcpodp.c b/tests/tcg/aarch64/dcpodp.c new file mode 100644 index 0000000000..2cf7df2e07 --- /dev/null +++ b/tests/tcg/aarch64/dcpodp.c @@ -0,0 +1,63 @@ +/* + * Test execution of DC CVADP instruction. + * + * Copyright (c) 2023 Zhuojia Shen + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include +#include +#include + +#ifndef HWCAP2_DCPODP +#define HWCAP2_DCPODP (1 << 0) +#endif + +bool should_fail = false; + +static void signal_handler(int sig, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + uc->uc_mcontext.pc += 4; + } else { + exit(EXIT_FAILURE); + } +} + +static int do_dc_cvadp(void) +{ + struct sigaction sa = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = signal_handler, + }; + + sigemptyset(&sa.sa_mask); + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + asm volatile("dc cvadp, %0\n\t" :: "r"(&sa)); + + should_fail = true; + asm volatile("dc cvadp, %0\n\t" :: "r"(NULL)); + should_fail = false; + + return EXIT_SUCCESS; +} + +int main(void) +{ + if (getauxval(AT_HWCAP2) & HWCAP2_DCPODP) { + return do_dc_cvadp(); + } else { + printf("SKIP: no HWCAP2_DCPODP on this system\n"); + return EXIT_SUCCESS; + } +} diff --git a/tests/tcg/aarch64/dcpop.c b/tests/tcg/aarch64/dcpop.c new file mode 100644 index 0000000000..a332a804a4 --- /dev/null +++ b/tests/tcg/aarch64/dcpop.c @@ -0,0 +1,63 @@ +/* + * Test execution of DC CVAP instruction. + * + * Copyright (c) 2023 Zhuojia Shen + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include +#include +#include + +#ifndef HWCAP_DCPOP +#define HWCAP_DCPOP (1 << 16) +#endif + +bool should_fail = false; + +static void signal_handler(int sig, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + uc->uc_mcontext.pc += 4; + } else { + exit(EXIT_FAILURE); + } +} + +static int do_dc_cvap(void) +{ + struct sigaction sa = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = signal_handler, + }; + + sigemptyset(&sa.sa_mask); + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + asm volatile("dc cvap, %0\n\t" :: "r"(&sa)); + + should_fail = true; + asm volatile("dc cvap, %0\n\t" :: "r"(NULL)); + should_fail = false; + + return EXIT_SUCCESS; +} + +int main(void) +{ + if (getauxval(AT_HWCAP) & HWCAP_DCPOP) { + return do_dc_cvap(); + } else { + printf("SKIP: no HWCAP_DCPOP on this system\n"); + return EXIT_SUCCESS; + } +} From f9ac778898cb28307e0f91421aba34d43c34b679 Mon Sep 17 00:00:00 2001 From: Zhuojia Shen Date: Tue, 6 Jun 2023 10:19:40 +0100 Subject: [PATCH 173/412] target/arm: trap DCC access in user mode emulation Accessing EL0-accessible Debug Communication Channel (DCC) registers in user mode emulation is currently enabled. However, it does not match Linux behavior as Linux sets MDSCR_EL1.TDCC on startup to disable EL0 access to DCC (see __cpu_setup() in arch/arm64/mm/proc.S). This patch fixes access_tdcc() to check MDSCR_EL1.TDCC for EL0 and sets MDSCR_EL1.TDCC for user mode emulation to match Linux. Signed-off-by: Zhuojia Shen Reviewed-by: Richard Henderson Message-id: DS7PR12MB630905198DD8E69F6817544CAC4EA@DS7PR12MB6309.namprd12.prod.outlook.com Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 ++ target/arm/debug_helper.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 5182ed0c91..4d5bb57f07 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -289,6 +289,8 @@ static void arm_cpu_reset_hold(Object *obj) * This is not yet exposed from the Linux kernel in any way. */ env->cp15.sctlr_el[1] |= SCTLR_TSCXT; + /* Disable access to Debug Communication Channel (DCC). */ + env->cp15.mdscr_el1 |= 1 << 12; #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index d41cc643b1..8362462a07 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -842,12 +842,14 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, * is implemented then these are controlled by MDCR_EL2.TDCC for * EL2 and MDCR_EL3.TDCC for EL3. They are also controlled by * the general debug access trap bits MDCR_EL2.TDA and MDCR_EL3.TDA. + * For EL0, they are also controlled by MDSCR_EL1.TDCC. */ static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdscr_el1_tdcc = extract32(env->cp15.mdscr_el1, 12, 1); bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || (arm_hcr_el2_eff(env) & HCR_TGE); bool mdcr_el2_tdcc = cpu_isar_feature(aa64_fgt, env_archcpu(env)) && @@ -855,6 +857,9 @@ static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, bool mdcr_el3_tdcc = cpu_isar_feature(aa64_fgt, env_archcpu(env)) && (env->cp15.mdcr_el3 & MDCR_TDCC); + if (el < 1 && mdscr_el1_tdcc) { + return CP_ACCESS_TRAP; + } if (el < 2 && (mdcr_el2_tda || mdcr_el2_tdcc)) { return CP_ACCESS_TRAP_EL2; } From e8e4298feadae7924cf7600bb3bcc5b0a8d7cbe9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 5 Jun 2023 17:30:38 +0200 Subject: [PATCH 174/412] tests: Use separate virtual environment for avocado This reverts commits eea2d141179 ("Makefile: remove $(TESTS_PYTHON)", 2023-05-26) and 9c6692db550 ("tests: Use configure-provided pyvenv for tests", 2023-05-18). Right now, there is a conflict between wanting a ">=" constraint when using a distro-provided package and wanting a "==" constraint when installing Avocado from PyPI; this would provide the best of both worlds in terms of resiliency for both distros that have required packages and distros that don't. The conflict is visible also for meson, where we would like to install the latest 0.63.x version but also accept a distro 1.1.x version. But it is worse for avocado, for two reasons: 1) we cannot use an "==" constraint to install avocado if the venv includes a system avocado. The distro will package plugins that have "==" constraints on the version that is included in the distro, and, using "pip install avocado==88.1" on a venv that includes system packages will result in this error: ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. avocado-framework-plugin-varianter-yaml-to-mux 98.0 requires avocado-framework==98.0, but you have avocado-framework 88.1 which is incompatible. avocado-framework-plugin-result-html 98.0 requires avocado-framework==98.0, but you have avocado-framework 88.1 which is incompatible. make[1]: Leaving directory '/home/berrange/src/virt/qemu/build' 2) we cannot use ">=" either if the venv does _not_ include a system avocado, because that would result in the installation of v101.0 which is the one we've just reverted. So the idea is to encode the dependencies as an (acceptable, locked) tuple, like this hypothetical TOML that would be committed inside python/ and used by mkvenv.py: [meson] meson = { minimum = "0.63.0", install = "0.63.3", canary = "meson" } [docs] # 6.0 drops support for Python 3.7 sphinx = { minimum = "1.6", install = "<6.0", canary = "sphinx-build" } sphinx_rtd_theme = { minimum = "0.5" } [avocado] avocado-framework = { minimum = "88.1", install = "88.1", canary = "avocado" } Once this is implemented, it would also be possible to install avocado in pyvenv/ using "mkvenv.py ensure", thus using the distro package on Fedora and CentOS Stream (the only distros where it's available). But until this is implemented, keep avocado in a separate venv. There is still the benefit of using a single python for meson custom_targets and for sphinx. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 6 +++--- docs/devel/acpi-bits.rst | 6 +++--- docs/devel/testing.rst | 14 +++++++------- .../ci/org.centos/stream/8/x86_64/test-avocado | 4 ++-- scripts/device-crash-test | 2 +- tests/Makefile.include | 16 +++++++++------- tests/requirements.txt | 7 ++----- tests/vm/Makefile.include | 2 +- 8 files changed, 28 insertions(+), 29 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 0f1be14cb6..1922caf536 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -103,7 +103,7 @@ crash-test-debian: script: - cd build - make NINJA=":" check-venv - - pyvenv/bin/python3 scripts/device-crash-test -q --tcg-only ./qemu-system-i386 + - tests/venv/bin/python3 scripts/device-crash-test -q --tcg-only ./qemu-system-i386 build-system-fedora: extends: @@ -146,8 +146,8 @@ crash-test-fedora: script: - cd build - make NINJA=":" check-venv - - pyvenv/bin/python3 scripts/device-crash-test -q ./qemu-system-ppc - - pyvenv/bin/python3 scripts/device-crash-test -q ./qemu-system-riscv32 + - tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-ppc + - tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-riscv32 build-system-centos: extends: diff --git a/docs/devel/acpi-bits.rst b/docs/devel/acpi-bits.rst index 9677b0098f..22e2580200 100644 --- a/docs/devel/acpi-bits.rst +++ b/docs/devel/acpi-bits.rst @@ -61,19 +61,19 @@ Under ``tests/avocado/`` as the root we have: :: $ make check-venv (needed only the first time to create the venv) - $ ./pyvenv/bin/avocado run -t acpi tests/avocado + $ ./tests/venv/bin/avocado run -t acpi tests/avocado The above will run all acpi avocado tests including this one. In order to run the individual tests, perform the following: :: - $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py --tap - + $ ./tests/venv/bin/avocado run tests/avocado/acpi-bits.py --tap - The above will produce output in tap format. You can omit "--tap -" in the end and it will produce output like the following: :: - $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py + $ ./tests/venv/bin/avocado run tests/avocado/acpi-bits.py Fetching asset from tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits JOB ID : eab225724da7b64c012c65705dc2fa14ab1defef JOB LOG : /home/anisinha/avocado/job-results/job-2022-10-10T17.58-eab2257/job.log diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 2cafec4178..203facb417 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -888,9 +888,9 @@ You can run the avocado tests simply by executing: make check-avocado -This involves the automatic installation, from PyPI, of all the -necessary avocado-framework dependencies into the QEMU venv within the -build tree (at ``./pyvenv``). Test results are also saved within the +This involves the automatic creation of Python virtual environment +within the build tree (at ``tests/venv``) which will have all the +right dependencies, and will save tests results also within the build tree (at ``tests/results``). Note: the build environment must be using a Python 3 stack, and have @@ -947,7 +947,7 @@ may be invoked by running: .. code:: - pyvenv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ + tests/venv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ Note that if ``make check-avocado`` was not executed before, it is possible to create the Python virtual environment with the dependencies @@ -962,20 +962,20 @@ a test file. To run tests from a single file within the build tree, use: .. code:: - pyvenv/bin/avocado run tests/avocado/$TESTFILE + tests/venv/bin/avocado run tests/avocado/$TESTFILE To run a single test within a test file, use: .. code:: - pyvenv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME + tests/venv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME Valid test names are visible in the output from any previous execution of Avocado or ``make check-avocado``, and can also be queried using: .. code:: - pyvenv/bin/avocado list tests/avocado + tests/venv/bin/avocado list tests/avocado Manual Installation ~~~~~~~~~~~~~~~~~~~ diff --git a/scripts/ci/org.centos/stream/8/x86_64/test-avocado b/scripts/ci/org.centos/stream/8/x86_64/test-avocado index 73e7a1a312..e0443fc8ae 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/test-avocado +++ b/scripts/ci/org.centos/stream/8/x86_64/test-avocado @@ -4,7 +4,7 @@ # KVM and x86_64, or tests that are generic enough to be valid for all # targets. Such a test list can be generated with: # -# ./pyvenv/bin/avocado list --filter-by-tags-include-empty \ +# ./tests/venv/bin/avocado list --filter-by-tags-include-empty \ # --filter-by-tags-include-empty-key -t accel:kvm,arch:x86_64 \ # tests/avocado/ # @@ -22,7 +22,7 @@ # - tests/avocado/virtio_check_params.py:VirtioMaxSegSettingsCheck.test_machine_types # make get-vm-images -./pyvenv/bin/avocado run \ +./tests/venv/bin/avocado run \ --job-results-dir=tests/results/ \ tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_i440fx_kvm \ tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_q35_kvm \ diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 353aa575d7..b74d887331 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -43,7 +43,7 @@ except ModuleNotFoundError as exc: print(f"Module '{exc.name}' not found.") print(" Try 'make check-venv' from your build directory,") print(" and then one way to run this script is like so:") - print(f' > $builddir/pyvenv/bin/python3 "{path}"') + print(f' > $builddir/tests/venv/bin/python3 "{path}"') sys.exit(1) logger = logging.getLogger('device-crash-test') diff --git a/tests/Makefile.include b/tests/Makefile.include index 8294a44816..9422ddaece 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -89,9 +89,10 @@ distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) # Build up our target list from the filtered list of ninja targets TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets))) -TESTS_VENV_TOKEN=$(BUILD_DIR)/pyvenv/tests.group +TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results +TESTS_PYTHON=$(TESTS_VENV_DIR)/bin/python3 ifndef AVOCADO_TESTS AVOCADO_TESTS=tests/avocado endif @@ -107,10 +108,11 @@ else endif quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ - $(PYTHON) -m pip -q --disable-pip-version-check $1, \ + $(TESTS_PYTHON) -m pip -q --disable-pip-version-check $1, \ "VENVPIP","$1") -$(TESTS_VENV_TOKEN): $(TESTS_VENV_REQ) +$(TESTS_VENV_DIR): $(TESTS_VENV_REQ) + $(call quiet-command, $(PYTHON) -m venv $@, VENV, $@) $(call quiet-venv-pip,install -e "$(SRC_PATH)/python/") $(call quiet-venv-pip,install -r $(TESTS_VENV_REQ)) $(call quiet-command, touch $@) @@ -119,7 +121,7 @@ $(TESTS_RESULTS_DIR): $(call quiet-command, mkdir -p $@, \ MKDIR, $@) -check-venv: $(TESTS_VENV_TOKEN) +check-venv: $(TESTS_VENV_DIR) FEDORA_31_ARCHES_TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGETS))) FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(FEDORA_31_ARCHES_TARGETS)) @@ -129,7 +131,7 @@ FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) # download one specific Fedora 31 image get-vm-image-fedora-31-%: check-venv $(call quiet-command, \ - $(PYTHON) -m avocado vmimage get \ + $(TESTS_PYTHON) -m avocado vmimage get \ --distro=fedora --distro-version=31 --arch=$*, \ "AVOCADO", "Downloading avocado tests VM image for $*") @@ -138,7 +140,7 @@ get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOW check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(call quiet-command, \ - $(PYTHON) -m avocado \ + $(TESTS_PYTHON) -m avocado \ --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ $(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \ --filter-by-tags-include-empty-key) \ @@ -161,7 +163,7 @@ check: check-build: run-ninja check-clean: - rm -rf $(TESTS_RESULTS_DIR) + rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) clean: check-clean clean-tcg distclean: distclean-tcg diff --git a/tests/requirements.txt b/tests/requirements.txt index 07e713ef5a..0ba561b6bd 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,9 +1,6 @@ # Add Python module requirements, one per line, to be installed -# in the qemu build_dir/pyvenv Python virtual environment. For more info, +# in the tests/venv Python virtual environment. For more info, # refer to: https://pip.pypa.io/en/stable/user_guide/#id1 -# -# Note that qemu.git/python/ is implicitly installed to this venv when -# 'make check-venv' is run, and will persist until configure is run -# again. +# Note that qemu.git/python/ is always implicitly installed. avocado-framework==88.1 pycdlib==1.11.0 diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index f0f5d32fb0..c2a8ca1c17 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -5,7 +5,7 @@ ifeq ($(realpath $(SRC_PATH)),$(realpath .)) VM_PYTHON = PYTHONPATH=$(SRC_PATH)/python /usr/bin/env python3 VM_VENV = else -VM_PYTHON = $(PYTHON) +VM_PYTHON = $(TESTS_PYTHON) VM_VENV = check-venv endif From 47a90a51a9c24ba10c58c0cd09d2117cf9e3fde2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 6 Jun 2023 10:14:45 +0200 Subject: [PATCH 175/412] mkvenv: always pass locally-installed packages to pip Let pip decide whether a new version should be installed or the current one is okay. This ensures that the virtual environment is updated (either upgraded or downgraded) whenever a new version of a package is requested. The hardest part here is figuring out if a package is installed in the venv (which also has to be done twice to account for the presence of either setuptools in Python <3.8, or importlib in Python >=3.8). Suggested-by: Peter Maydell Cc: John Snow Signed-off-by: Paolo Bonzini --- python/scripts/mkvenv.py | 76 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index 3a9aef46a5..a47f1eaf5d 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -553,6 +553,74 @@ def pkgname_from_depspec(dep_spec: str) -> str: return match.group(0) +def _get_path_importlib(package: str) -> Optional[str]: + # pylint: disable=import-outside-toplevel + # pylint: disable=no-name-in-module + # pylint: disable=import-error + try: + # First preference: Python 3.8+ stdlib + from importlib.metadata import ( # type: ignore + PackageNotFoundError, + distribution, + ) + except ImportError as exc: + logger.debug("%s", str(exc)) + # Second preference: Commonly available PyPI backport + from importlib_metadata import ( # type: ignore + PackageNotFoundError, + distribution, + ) + + try: + return str(distribution(package).locate_file(".")) + except PackageNotFoundError: + return None + + +def _get_path_pkg_resources(package: str) -> Optional[str]: + # pylint: disable=import-outside-toplevel + # Bundled with setuptools; has a good chance of being available. + import pkg_resources + + try: + return str(pkg_resources.get_distribution(package).location) + except pkg_resources.DistributionNotFound: + return None + + +def _get_path(package: str) -> Optional[str]: + try: + return _get_path_importlib(package) + except ImportError as exc: + logger.debug("%s", str(exc)) + + try: + return _get_path_pkg_resources(package) + except ImportError as exc: + logger.debug("%s", str(exc)) + raise Ouch( + "Neither importlib.metadata nor pkg_resources found. " + "Use Python 3.8+, or install importlib-metadata or setuptools." + ) from exc + + +def _path_is_prefix(prefix: Optional[str], path: str) -> bool: + try: + return ( + prefix is not None and os.path.commonpath([prefix, path]) == prefix + ) + except ValueError: + return False + + +def _is_system_package(package: str) -> bool: + path = _get_path(package) + return path is not None and not ( + _path_is_prefix(sysconfig.get_path("purelib"), path) + or _path_is_prefix(sysconfig.get_path("platlib"), path) + ) + + def _get_version_importlib(package: str) -> Optional[str]: # pylint: disable=import-outside-toplevel # pylint: disable=no-name-in-module @@ -741,8 +809,12 @@ def _do_ensure( for spec in dep_specs: matcher = distlib.version.LegacyMatcher(spec) ver = _get_version(matcher.name) - if ver is None or not matcher.match( - distlib.version.LegacyVersion(ver) + if ( + ver is None + # Always pass installed package to pip, so that they can be + # updated if the requested version changes + or not _is_system_package(matcher.name) + or not matcher.match(distlib.version.LegacyVersion(ver)) ): absent.append(spec) else: From 50cfed80ecc01ea4300ed4b0ea5b567f381b9421 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 May 2023 12:36:28 +0200 Subject: [PATCH 176/412] configure: remove --with-git= option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scenario for which --with-git= was introduced was to use a SOCKS proxy such as tsocks. However, this was back in 2017 when QEMU's submodules used the git:// protocol, and it is not as important when using the "smart HTTP" backend; for example, neither "meson subprojects download" nor scripts/checkpatch.pl obey the GIT environment variable. So remove the knob, but test for the presence of git in the configure and git-submodule.sh scripts, and suggest using --with-git-submodules=validate + a manual invocation of git-submodule.sh when git does not work. Hopefully in the future the GIT environment variable will be supported by Meson. Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Signed-off-by: Paolo Bonzini --- Makefile | 2 +- configure | 8 ++------ meson.build | 1 - .../ci/org.centos/stream/8/x86_64/configure | 2 -- scripts/git-submodule.sh | 19 ++++++++++--------- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 08fb6a3b05..8005f1cc53 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ Makefile: .git-submodule-status git-submodule-update: ifneq ($(GIT_SUBMODULES_ACTION),ignore) $(call quiet-command, \ - (GIT="$(GIT)" "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \ + (GIT=git "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \ "GIT","$(GIT_SUBMODULES)") endif diff --git a/configure b/configure index d674a96673..92bacc756c 100755 --- a/configure +++ b/configure @@ -751,8 +751,6 @@ for opt do ;; --enable-fdt=*) fdt="$optarg" ;; - --with-git=*) git="$optarg" - ;; --with-git-submodules=*) git_submodules_action="$optarg" ;; @@ -791,7 +789,7 @@ fi case $git_submodules_action in update|validate) - if test ! -e "$source_path/.git"; then + if test ! -e "$source_path/.git" || ! has git; then echo "ERROR: cannot $git_submodules_action git submodules without .git" exit 1 fi @@ -892,7 +890,6 @@ Advanced options (experts only): --python=PYTHON use specified python [$python] --ninja=NINJA use specified ninja [$ninja] --smbd=SMBD use specified smbd [$smbd] - --with-git=GIT use specified git [$git] --with-git-submodules=update update git submodules (default if .git dir exists) --with-git-submodules=validate fail if git submodules are not up to date --with-git-submodules=ignore do not update or check git submodules (default if no .git dir) @@ -1699,7 +1696,7 @@ fi ####################################### # generate config-host.mak -if ! (GIT="$git" "$source_path/scripts/git-submodule.sh" "$git_submodules_action" "$git_submodules"); then +if ! (GIT=git "$source_path/scripts/git-submodule.sh" "$git_submodules_action" "$git_submodules"); then exit 1 fi @@ -1709,7 +1706,6 @@ echo "# Automatically generated by configure - do not modify" > $config_host_mak echo >> $config_host_mak echo all: >> $config_host_mak -echo "GIT=$git" >> $config_host_mak echo "GIT_SUBMODULES=$git_submodules" >> $config_host_mak echo "GIT_SUBMODULES_ACTION=$git_submodules_action" >> $config_host_mak diff --git a/meson.build b/meson.build index 6767a0d3fa..4302c01923 100644 --- a/meson.build +++ b/meson.build @@ -4011,7 +4011,6 @@ summary(summary_info, bool_yn: true, section: 'Directories') # Host binaries summary_info = {} -summary_info += {'git': config_host['GIT']} summary_info += {'python': '@0@ (version: @1@)'.format(python.full_path(), python.language_version())} summary_info += {'sphinx-build': sphinx_build} if config_host.has_key('HAVE_GDB_BIN') diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure index 6e8983f39c..de76510978 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ b/scripts/ci/org.centos/stream/8/x86_64/configure @@ -29,14 +29,12 @@ --extra-cflags="-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection" \ --with-suffix="qemu-kvm" \ --firmwarepath=/usr/share/qemu-firmware \ ---with-git=meson \ --with-git-submodules=update \ --target-list="x86_64-softmmu" \ --block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \ --audio-drv-list="" \ --block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \ --with-coroutine=ucontext \ ---with-git=git \ --tls-priority=@QEMU,SYSTEM \ --disable-attr \ --disable-auth-pam \ diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index 7be41f5948..0ce1efc44e 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -12,7 +12,7 @@ maybe_modules="$@" # if --with-git-submodules=ignore, do nothing test "$command" = "ignore" && exit 0 -test -z "$GIT" && GIT=git +test -z "$GIT" && GIT=$(command -v git) cd "$(dirname "$0")/.." @@ -21,19 +21,14 @@ update_error() { echo echo "Unable to automatically checkout GIT submodules '$modules'." echo "If you require use of an alternative GIT binary (for example to" - echo "enable use of a transparent proxy), then please specify it by" - echo "running configure by with the '--with-git' argument. e.g." - echo - echo " $ ./configure --with-git='tsocks git'" - echo - echo "Alternatively you may disable automatic GIT submodule checkout" - echo "with:" + echo "enable use of a transparent proxy), please disable automatic" + echo "GIT submodule checkout with:" echo echo " $ ./configure --with-git-submodules=validate" echo echo "and then manually update submodules prior to running make, with:" echo - echo " $ scripts/git-submodule.sh update $modules" + echo " $ GIT='tsocks git' scripts/git-submodule.sh update $modules" echo exit 1 } @@ -57,6 +52,12 @@ then exit 1 fi +if test -n "$maybe_modules" && test -z "$GIT" +then + echo "$0: unexpectedly called with submodules but git binary not found" + exit 1 +fi + modules="" for m in $maybe_modules do From ac4ccac740a824539a92ef397bb5efa58bd8cbe5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 18 May 2023 16:11:29 +0200 Subject: [PATCH 177/412] configure: rename --enable-pypi to --enable-download, control subprojects too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The behavior of --{enable,disable}-pypi is similar to that of -Dwrapmode={default,nodownload} respectively. In particular, in both cases a feature needs to be explicitly enabled for the dependency to be downloaded. So, use a single option to control both cases. Now, --enable-slirp will trigger cloning and building of libslirp if the .pc file is not found on the machine. Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Signed-off-by: Paolo Bonzini --- .gitignore | 2 -- configure | 17 +++++++---------- meson.build | 12 ++++++++---- subprojects/.gitignore | 3 +++ 4 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 subprojects/.gitignore diff --git a/.gitignore b/.gitignore index 1ea59f4819..61fa39967b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,3 @@ GTAGS *.swp *.patch *.gcov - -/subprojects/slirp diff --git a/configure b/configure index 92bacc756c..5227421231 100755 --- a/configure +++ b/configure @@ -266,7 +266,7 @@ bsd_user="" plugins="$default_feature" ninja="" python= -pypi="enabled" +download="enabled" bindir="bin" skip_meson=no vfio_user_server="disabled" @@ -754,9 +754,9 @@ for opt do --with-git-submodules=*) git_submodules_action="$optarg" ;; - --disable-pypi) pypi="disabled" + --disable-download) download="disabled" ;; - --enable-pypi) pypi="enabled" + --enable-download) download="enabled" ;; --enable-plugins) if test "$mingw32" = "yes"; then error_exit "TCG plugins not currently supported on Windows platforms" @@ -960,7 +960,7 @@ python="$(command -v "$python")" # - venv is allowed to use system packages; # - all setup can be performed offline; # - missing packages may be fetched from PyPI, -# unless --disable-pypi is passed. +# unless --disable-download is passed. # - pip is not installed into the venv when possible, # but ensurepip is called as a fallback when necessary. @@ -977,7 +977,7 @@ python="$python -B" mkvenv="$python ${source_path}/python/scripts/mkvenv.py" mkvenv_flags="" -if test "$pypi" = "enabled" ; then +if test "$download" = "enabled" ; then mkvenv_flags="--online" fi @@ -1000,7 +1000,7 @@ meson="$(cd pyvenv/bin; pwd)/meson" # Conditionally ensure Sphinx is installed. mkvenv_flags="" -if test "$pypi" = "enabled" -a "$docs" = "enabled" ; then +if test "$download" = "enabled" -a "$docs" = "enabled" ; then mkvenv_flags="--online" fi @@ -1940,11 +1940,8 @@ if test "$skip_meson" = no; then rm -rf meson-private meson-info meson-logs - # Prevent meson from automatically downloading wrapped subprojects when missing. - # You can use 'meson subprojects download' before running configure. - meson_option_add "--wrap-mode=nodownload" - # Built-in options + test "$download" = "disabled" && meson_option_add "--wrap-mode=nodownload" test "$bindir" != "bin" && meson_option_add "-Dbindir=$bindir" test "$default_feature" = no && meson_option_add -Dauto_features=disabled test "$static" = yes && meson_option_add -Dprefer_static=true diff --git a/meson.build b/meson.build index 4302c01923..f8af4b02e4 100644 --- a/meson.build +++ b/meson.build @@ -3985,8 +3985,15 @@ endif # Configuration summary # ######################### -# Directories +# Build environment summary_info = {} +summary_info += {'Build directory': meson.current_build_dir()} +summary_info += {'Source path': meson.current_source_dir()} +summary_info += {'GIT submodules': config_host['GIT_SUBMODULES']} +summary_info += {'Download dependencies': get_option('wrap_mode') != 'nodownload'} +summary(summary_info, bool_yn: true, section: 'Build environment') + +# Directories summary_info += {'Install prefix': get_option('prefix')} summary_info += {'BIOS directory': qemu_datadir} pathsep = targetos == 'windows' ? ';' : ':' @@ -4004,9 +4011,6 @@ else summary_info += {'local state directory': 'queried at runtime'} endif summary_info += {'Doc directory': get_option('prefix') / get_option('docdir')} -summary_info += {'Build directory': meson.current_build_dir()} -summary_info += {'Source path': meson.current_source_dir()} -summary_info += {'GIT submodules': config_host['GIT_SUBMODULES']} summary(summary_info, bool_yn: true, section: 'Directories') # Host binaries diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 0000000000..7560ebb0b1 --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1,3 @@ +/packagecache + +/slirp From fdb8fd8cb915647be7f7f2e2f0c530ed06ca9b01 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 May 2023 17:10:29 +0200 Subject: [PATCH 178/412] git-submodule: allow partial update of .git-submodule-status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow a specific subdirectory to run git-submodule.sh with only a subset of submodules, without removing the others from the .git-submodule-status file. This also allows scripts/git-submodule.sh to be more lenient: validating an empty set of submodules is not a mistake. Reviewed-by: Alex Bennée Signed-off-by: Paolo Bonzini --- scripts/git-submodule.sh | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index 0ce1efc44e..b7d8f05352 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -72,12 +72,8 @@ done case "$command" in status|validate) - if test -z "$maybe_modules" - then - test -s ${substat} && validate_error "$command" || exit 0 - fi - test -f "$substat" || validate_error "$command" + test -z "$maybe_modules" && exit 0 for module in $modules; do CURSTATUS=$($GIT submodule status $module) OLDSTATUS=$(cat $substat | grep $module) @@ -88,17 +84,23 @@ status|validate) exit 0 ;; update) - if test -z "$maybe_modules" - then - test -e $substat || touch $substat - exit 0 - fi + test -e $substat || touch $substat + test -z "$maybe_modules" && exit 0 $GIT submodule update --init $modules 1>/dev/null test $? -ne 0 && update_error "failed to update modules" - $GIT submodule status $modules > "${substat}" - test $? -ne 0 && update_error "failed to save git submodule status" >&2 + (while read -r; do + for module in $modules; do + case $REPLY in + *" $module "*) continue 2 ;; + esac + done + printf '%s\n' "$REPLY" + done + $GIT submodule status $modules + test $? -ne 0 && update_error "failed to save git submodule status" >&2) < $substat > $substat.new + mv -f $substat.new $substat ;; esac From d120116b5d6e81831332f807028a29c5e5815a6a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 May 2023 17:27:48 +0200 Subject: [PATCH 179/412] build: log submodule update from git-submodule.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print exactly which submodules have been updated, by reusing the logic of "git-submodule.sh validate" after executing "git submodule update --init'. Reviewed-by: Alex Bennée Signed-off-by: Paolo Bonzini --- Makefile | 4 +--- scripts/git-submodule.sh | 16 +++++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 8005f1cc53..d68196acb9 100644 --- a/Makefile +++ b/Makefile @@ -52,9 +52,7 @@ Makefile: .git-submodule-status .PHONY: git-submodule-update git-submodule-update: ifneq ($(GIT_SUBMODULES_ACTION),ignore) - $(call quiet-command, \ - (GIT=git "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \ - "GIT","$(GIT_SUBMODULES)") + $(quiet-@)GIT=git "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES) endif # 0. ensure the build tree is okay diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index b7d8f05352..38b55c90e1 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -46,6 +46,13 @@ validate_error() { exit 1 } +check_updated() { + local CURSTATUS OLDSTATUS + CURSTATUS=$($GIT submodule status $module) + OLDSTATUS=$(grep $module $substat) + test "$CURSTATUS" = "$OLDSTATUS" +} + if test -n "$maybe_modules" && ! test -e ".git" then echo "$0: unexpectedly called with submodules but no git checkout exists" @@ -75,11 +82,7 @@ status|validate) test -f "$substat" || validate_error "$command" test -z "$maybe_modules" && exit 0 for module in $modules; do - CURSTATUS=$($GIT submodule status $module) - OLDSTATUS=$(cat $substat | grep $module) - if test "$CURSTATUS" != "$OLDSTATUS"; then - validate_error "$command" - fi + check_updated $module || validate_error "$command" done exit 0 ;; @@ -89,6 +92,9 @@ update) $GIT submodule update --init $modules 1>/dev/null test $? -ne 0 && update_error "failed to update modules" + for module in $modules; do + check_updated $module || echo Updated "$module" + done (while read -r; do for module in $modules; do From 2019cabfee08dd49c28359b6fd0bac63fb12df9b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 18 May 2023 16:50:00 +0200 Subject: [PATCH 180/412] meson: subprojects: replace submodules with wrap files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compared to submodules, .wrap files have several advantages: * option parsing and downloading is delegated to meson * the commit is stored in a text file instead of a magic entry in the git tree object * we could stop shipping external dependencies that are only used as a fallback, but not break compilation on platforms that lack them. For example it may make sense to download dtc at build time, controlled by --enable-download, even when building from a tarball. Right now, this patch does the opposite: make-release treats dtc like libvfio-user (which is not stable API and therefore hasn't found its way into any distros) and keycodemap (which is a copylib, for better or worse). dependency() can fall back to a wrap automatically. However, this is only possible for libraries that come with a .pc file, and this is not very common for libfdt even though the upstream project in principle provides it; it also removes the control that we provide with --enable-fdt={system,internal}. Therefore, the logic to pick system vs. internal libfdt is left untouched. --enable-fdt=git is removed; it was already a synonym for --enable-fdt=internal. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest-template.yml | 1 + .gitmodules | 9 ---- configure | 77 ++++++++++++----------------- meson.build | 15 +----- scripts/archive-source.sh | 11 ++++- scripts/make-release | 5 ++ subprojects/.gitignore | 3 ++ subprojects/dtc | 1 - subprojects/dtc.wrap | 4 ++ subprojects/keycodemapdb | 1 - subprojects/keycodemapdb.wrap | 4 ++ subprojects/libvfio-user | 1 - subprojects/libvfio-user.wrap | 4 ++ 13 files changed, 63 insertions(+), 73 deletions(-) delete mode 160000 subprojects/dtc create mode 100644 subprojects/dtc.wrap delete mode 160000 subprojects/keycodemapdb create mode 100644 subprojects/keycodemapdb.wrap delete mode 160000 subprojects/libvfio-user create mode 100644 subprojects/libvfio-user.wrap diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index c9f2e737c0..3c997d7265 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -44,6 +44,7 @@ script: - scripts/git-submodule.sh update $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) + - meson subprojects download $(cd build/subprojects && echo *) - cd build - find . -type f -exec touch {} + # Avoid recompiling by hiding ninja with NINJA=":" diff --git a/.gitmodules b/.gitmodules index f8b2ddf387..a934c85e4e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,9 +13,6 @@ [submodule "roms/qemu-palcode"] path = roms/qemu-palcode url = https://gitlab.com/qemu-project/qemu-palcode.git -[submodule "subprojects/dtc"] - path = subprojects/dtc - url = https://gitlab.com/qemu-project/dtc.git [submodule "roms/u-boot"] path = roms/u-boot url = https://gitlab.com/qemu-project/u-boot.git @@ -25,9 +22,6 @@ [submodule "roms/QemuMacDrivers"] path = roms/QemuMacDrivers url = https://gitlab.com/qemu-project/QemuMacDrivers.git -[submodule "subprojects/keycodemapdb"] - path = subprojects/keycodemapdb - url = https://gitlab.com/qemu-project/keycodemapdb.git [submodule "roms/seabios-hppa"] path = roms/seabios-hppa url = https://gitlab.com/qemu-project/seabios-hppa.git @@ -55,6 +49,3 @@ [submodule "tests/lcitool/libvirt-ci"] path = tests/lcitool/libvirt-ci url = https://gitlab.com/libvirt/libvirt-ci.git -[submodule "subprojects/libvfio-user"] - path = subprojects/libvfio-user - url = https://gitlab.com/qemu-project/libvfio-user.git diff --git a/configure b/configure index 5227421231..45d43ddbd9 100755 --- a/configure +++ b/configure @@ -253,7 +253,7 @@ else git_submodules_action="ignore" fi -git_submodules="subprojects/keycodemapdb" +git_submodules="" git="git" debug_tcg="no" docs="auto" @@ -269,7 +269,6 @@ python= download="enabled" bindir="bin" skip_meson=no -vfio_user_server="disabled" use_containers="yes" gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb") gdb_arches="" @@ -281,16 +280,13 @@ unset target_list_exclude # The following Meson options are handled manually (still they # are included in the automatically generated help message) - -# 1. Track which submodules are needed -fdt="auto" - -# 2. Automatically enable/disable other options +# because they automatically enable/disable other options tcg="auto" cfi="false" -# 3. Need to check for -static-pie before Meson runs. Also, -# Meson has PIE as a boolean rather than enabled/disabled/auto. +# Meson has PIE as a boolean rather than enabled/disabled/auto, +# and we also need to check for -static-pie before Meson runs +# which requires knowing whether --static is enabled. pie="" static="no" @@ -743,14 +739,6 @@ for opt do ;; --disable-cfi) cfi="false" ;; - --disable-fdt) fdt="disabled" - ;; - --enable-fdt) fdt="enabled" - ;; - --enable-fdt=git) fdt="internal" - ;; - --enable-fdt=*) fdt="$optarg" - ;; --with-git-submodules=*) git_submodules_action="$optarg" ;; @@ -772,10 +760,6 @@ for opt do ;; --gdb=*) gdb_bin="$optarg" ;; - --enable-vfio-user-server) vfio_user_server="enabled" - ;; - --disable-vfio-user-server) vfio_user_server="disabled" - ;; # everything else has the same name in configure and meson --*) meson_option_parse "$opt" "$optarg" ;; @@ -787,6 +771,32 @@ if test "$plugins" = "yes" -a "$tcg" = "disabled"; then error_exit "Can't enable plugins on non-TCG builds" fi +if ! test -f "$source_path/subprojects/keycodemapdb/README" \ + && test -f "$download" = disabled +then + echo + echo "ERROR: missing subprojects" + echo + if test -e "$source_path/.git"; then + echo "--disable-download specified but subprojects were not" + echo 'checked out. Please invoke "meson subprojects download"' + echo "before configuring QEMU, or remove --disable-download" + echo "from the command line." + else + echo "This is not a GIT checkout but subproject content appears to" + echo "be missing. Do not use 'git archive' or GitHub download links" + echo "to acquire QEMU source archives. Non-GIT builds are only" + echo "supported with source archives linked from:" + echo + echo " https://www.qemu.org/download/#source" + echo + echo "Developers working with GIT can use scripts/archive-source.sh" + echo "if they need to create valid source archives." + fi + echo + exit 1 +fi + case $git_submodules_action in update|validate) if test ! -e "$source_path/.git" || ! has git; then @@ -795,7 +805,7 @@ case $git_submodules_action in fi ;; ignore) - if ! test -f "$source_path/subprojects/keycodemapdb/README" + if ! test -f "$source_path/tests/fp/berkeley-testfloat-3/README.md" then echo echo "ERROR: missing GIT submodules" @@ -1164,16 +1174,6 @@ EOF fi fi -########################################## -# fdt probe - -case "$fdt" in - auto | enabled | internal) - # Simpler to always update submodule, even if not needed. - git_submodules="${git_submodules} subprojects/dtc" - ;; -esac - ######################################## # check if ccache is interfering with # semantic analysis of macros @@ -1609,17 +1609,6 @@ write_target_makefile() { fi } -########################################## -# check for vfio_user_server - -case "$vfio_user_server" in - enabled ) - if test "$git_submodules_action" != "ignore"; then - git_submodules="${git_submodules} subprojects/libvfio-user" - fi - ;; -esac - ####################################### # cross-compiled firmware targets @@ -1951,12 +1940,10 @@ if test "$skip_meson" = no; then # QEMU options test "$cfi" != false && meson_option_add "-Dcfi=$cfi" test "$docs" != auto && meson_option_add "-Ddocs=$docs" - test "$fdt" != auto && meson_option_add "-Dfdt=$fdt" test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE" test "$qemu_suffix" != qemu && meson_option_add "-Dqemu_suffix=$qemu_suffix" test "$smbd" != '' && meson_option_add "-Dsmbd=$smbd" test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg" - test "$vfio_user_server" != auto && meson_option_add "-Dvfio_user_server=$vfio_user_server" run_meson() { NINJA=$ninja $meson setup --prefix "$prefix" "$@" $cross_arg "$PWD" "$source_path" } diff --git a/meson.build b/meson.build index f8af4b02e4..74fb147fde 100644 --- a/meson.build +++ b/meson.build @@ -3031,14 +3031,7 @@ endif libvfio_user_dep = not_found if have_system and vfio_user_server_allowed - have_internal = fs.exists(meson.current_source_dir() / 'subprojects/libvfio-user/meson.build') - - if not have_internal - error('libvfio-user source not found - please pull git submodule') - endif - - libvfio_user_proj = subproject('libvfio-user') - + libvfio_user_proj = subproject('libvfio-user', required: true) libvfio_user_dep = libvfio_user_proj.get_variable('libvfio_user_dep') endif @@ -3066,12 +3059,6 @@ if fdt_required.length() > 0 or fdt_opt == 'enabled' endif if not fdt.found() assert(fdt_opt == 'internal') - have_internal = fs.exists(meson.current_source_dir() / 'subprojects/dtc/meson.build') - - if not have_internal - error('libfdt source not found - please pull git submodule') - endif - libfdt_proj = subproject('dtc', required: true, default_options: ['tools=false', 'yaml=disabled', 'python=disabled', 'default_library=static']) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index dba5ae05b6..b99cb66e41 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -26,8 +26,8 @@ sub_file="${sub_tdir}/submodule.tar" # independent of what the developer currently has initialized # in their checkout, because the build environment is completely # different to the host OS. -submodules="subprojects/dtc subprojects/keycodemapdb" -submodules="$submodules tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" +subprojects="dtc keycodemapdb libvfio-user" +submodules="tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" sub_deinit="" function cleanup() { @@ -70,4 +70,11 @@ for sm in $submodules; do tar --concatenate --file "$tar_file" "$sub_file" test $? -ne 0 && error "failed append submodule $sm to $tar_file" done + +for sp in $subprojects; do + meson subprojects download $sp + test $? -ne 0 && error "failed to download subproject $sp" + tar --append --file "$tar_file" --exclude=.git subprojects/$sp + test $? -ne 0 && error "failed to append subproject $sp to $tar_file" +done exit 0 diff --git a/scripts/make-release b/scripts/make-release index 44a9d86a04..0604e61b81 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -16,6 +16,9 @@ if [ $# -ne 2 ]; then exit 0 fi +# Only include wraps that are invoked with subproject() +SUBPROJECTS="dtc libvfio-user keycodemapdb" + src="$1" version="$2" destination=qemu-${version} @@ -26,6 +29,8 @@ git clone --single-branch -b "v${version}" -c advice.detachedHead=false \ pushd ${destination} git submodule update --init --single-branch +meson subprojects download $SUBPROJECTS + (cd roms/seabios && git describe --tags --long --dirty > .version) (cd roms/skiboot && ./make_version.sh > .version) # Fetch edk2 submodule's submodules, since it won't have access to them via diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 7560ebb0b1..935e9a72a8 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -1,3 +1,6 @@ /packagecache +/dtc +/keycodemapdb +/libvfio-user /slirp diff --git a/subprojects/dtc b/subprojects/dtc deleted file mode 160000 index b6910bec11..0000000000 --- a/subprojects/dtc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6910bec11614980a21e46fbccc35934b671bd81 diff --git a/subprojects/dtc.wrap b/subprojects/dtc.wrap new file mode 100644 index 0000000000..d1bc9174e9 --- /dev/null +++ b/subprojects/dtc.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/dtc.git +revision = b6910bec11614980a21e46fbccc35934b671bd81 +depth = 1 diff --git a/subprojects/keycodemapdb b/subprojects/keycodemapdb deleted file mode 160000 index f5772a62ec..0000000000 --- a/subprojects/keycodemapdb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f5772a62ec52591ff6870b7e8ef32482371f22c6 diff --git a/subprojects/keycodemapdb.wrap b/subprojects/keycodemapdb.wrap new file mode 100644 index 0000000000..dda7b0e571 --- /dev/null +++ b/subprojects/keycodemapdb.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/keycodemapdb.git +revision = f5772a62ec52591ff6870b7e8ef32482371f22c6 +depth = 1 diff --git a/subprojects/libvfio-user b/subprojects/libvfio-user deleted file mode 160000 index 0b28d20557..0000000000 --- a/subprojects/libvfio-user +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0b28d205572c80b568a1003db2c8f37ca333e4d7 diff --git a/subprojects/libvfio-user.wrap b/subprojects/libvfio-user.wrap new file mode 100644 index 0000000000..416955ca45 --- /dev/null +++ b/subprojects/libvfio-user.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/libvfio-user.git +revision = 0b28d205572c80b568a1003db2c8f37ca333e4d7 +depth = 1 From b11f9bd96f451f0e60b2b91e19b900ec7c673f29 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 May 2023 13:26:39 +0200 Subject: [PATCH 181/412] configure: move SLOF submodule handling to pc-bios/s390-ccw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the handling of the roms/SLOF submodule out of the main Makefile, since we are going to remove submodules from the build process of QEMU. Acked-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest-template.yml | 2 +- configure | 7 +++---- pc-bios/s390-ccw/Makefile | 11 +++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 3c997d7265..d01d504ec5 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -43,7 +43,7 @@ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest script: - scripts/git-submodule.sh update - $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) + roms/SLOF $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) - meson subprojects download $(cd build/subprojects && echo *) - cd build - find . -type f -exec touch {} + diff --git a/configure b/configure index 45d43ddbd9..de3904fb59 100755 --- a/configure +++ b/configure @@ -1662,7 +1662,8 @@ fi # Only build s390-ccw bios if the compiler has -march=z900 or -march=z10 # (which is the lowest architecture level that Clang supports) -if have_target s390x-softmmu && probe_target_compiler s390x-softmmu; then +if have_target s390x-softmmu && probe_target_compiler s390x-softmmu && \ + GIT=git "$source_path/scripts/git-submodule.sh" "$git_submodules_action" roms/SLOF >> config.log 2>&1; then write_c_skeleton do_compiler "$target_cc" $target_cc_cflags -march=z900 -o $TMPO -c $TMPC has_z900=$? @@ -1675,10 +1676,8 @@ if have_target s390x-softmmu && probe_target_compiler s390x-softmmu; then config_mak=pc-bios/s390-ccw/config-host.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/pc-bios/s390-ccw" >> $config_mak + echo "GIT_SUBMODULES_ACTION=$git_submodules_action" >> $config_mak write_target_makefile >> $config_mak - # SLOF is required for building the s390-ccw firmware on s390x, - # since it is using the libnet code from SLOF for network booting. - git_submodules="${git_submodules} roms/SLOF" fi fi diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 10e8f5cb63..9c5276f8ad 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -6,6 +6,8 @@ include config-host.mak CFLAGS = -O2 -g MAKEFLAGS += -rR +GIT_SUBMODULES = roms/SLOF + NULL := SPACE := $(NULL) # TARGET_PREFIX := $(patsubst %/,%:$(SPACE),$(TARGET_DIR)) @@ -80,3 +82,12 @@ clean: distclean: rm -f config-cc.mak + +.PHONY: git-submodule-update +$(SRC_PATH)/../../.git-submodule-status: git-submodule-update config-host.mak +Makefile: $(SRC_PATH)/../../.git-submodule-status + +git-submodule-update: +ifneq ($(GIT_SUBMODULES_ACTION),ignore) + $(quiet-@)GIT=git "$(SRC_PATH)/../../scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES) +endif From bf6903f6944fe75606e4c67d6962b407c7bb5656 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 May 2023 13:26:39 +0200 Subject: [PATCH 182/412] pc-bios/s390-ccw: always build network bootloader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the beginning, the network bootloader was considered experimental and thus optional, but it is well established nowadays and configure always checks for roms/SLOF before compiling pc-bios/s390-ccw. Therefore, it makes sense to always build it together with the other part of the s390-ccw bios. Reviewed-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- pc-bios/s390-ccw/Makefile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 9c5276f8ad..2e8cc015aa 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -67,12 +67,7 @@ s390-ccw.img: s390-ccw.elf $(OBJECTS): Makefile -ifneq ($(wildcard $(SRC_PATH)/../../roms/SLOF/lib/libnet),) include $(SRC_PATH)/netboot.mak -else -s390-netboot.img: - @echo "s390-netboot.img not built since roms/SLOF/ is not available." -endif ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS)) -include $(ALL_OBJS:%.o=%.d) From d2dfe0b506e47e1409b29b65acb098d707532149 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 May 2023 13:27:10 +0200 Subject: [PATCH 183/412] meson: subprojects: replace berkeley-{soft,test}float-3 with wraps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike other subprojects, these require an overlay directory to include meson rules to build the libraries. The rules are basically lifted from tests/fp/meson.build, with a few changes to create platform.h and publish a dependency. The build defines are passed through a subproject option, and posted back to users of the library via the dependency's compile_args. The only remaining user of GIT_SUBMODULES and GIT_SUBMODULES_ACTION is roms/SLOF, which is used to build pc-bios/s390-ccw. All other roms submodules are only present to satisfy the license on pre-built firmware blobs. Best reviewed with --color-moved. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- .gitmodules | 6 - configure | 27 - scripts/archive-source.sh | 22 +- scripts/make-release | 2 +- subprojects/.gitignore | 2 + subprojects/berkeley-softfloat-3.wrap | 5 + subprojects/berkeley-testfloat-3.wrap | 5 + .../berkeley-softfloat-3/meson.build | 339 +++++++++++ .../berkeley-softfloat-3/meson_options.txt | 1 + .../berkeley-testfloat-3/meson.build | 220 +++++++ .../berkeley-testfloat-3/meson_options.txt | 1 + tests/fp/meson.build | 541 +----------------- 12 files changed, 590 insertions(+), 581 deletions(-) create mode 100644 subprojects/berkeley-softfloat-3.wrap create mode 100644 subprojects/berkeley-testfloat-3.wrap create mode 100644 subprojects/packagefiles/berkeley-softfloat-3/meson.build create mode 100644 subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt create mode 100644 subprojects/packagefiles/berkeley-testfloat-3/meson.build create mode 100644 subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt diff --git a/.gitmodules b/.gitmodules index a934c85e4e..73cae4cd4d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,12 +28,6 @@ [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex url = https://gitlab.com/qemu-project/u-boot-sam460ex.git -[submodule "tests/fp/berkeley-testfloat-3"] - path = tests/fp/berkeley-testfloat-3 - url = https://gitlab.com/qemu-project/berkeley-testfloat-3.git -[submodule "tests/fp/berkeley-softfloat-3"] - path = tests/fp/berkeley-softfloat-3 - url = https://gitlab.com/qemu-project/berkeley-softfloat-3.git [submodule "roms/edk2"] path = roms/edk2 url = https://gitlab.com/qemu-project/edk2.git diff --git a/configure b/configure index de3904fb59..4dad32938d 100755 --- a/configure +++ b/configure @@ -805,28 +805,6 @@ case $git_submodules_action in fi ;; ignore) - if ! test -f "$source_path/tests/fp/berkeley-testfloat-3/README.md" - then - echo - echo "ERROR: missing GIT submodules" - echo - if test -e "$source_path/.git"; then - echo "--with-git-submodules=ignore specified but submodules were not" - echo "checked out. Please initialize and update submodules." - else - echo "This is not a GIT checkout but module content appears to" - echo "be missing. Do not use 'git archive' or GitHub download links" - echo "to acquire QEMU source archives. Non-GIT builds are only" - echo "supported with source archives linked from:" - echo - echo " https://www.qemu.org/download/#source" - echo - echo "Developers working with GIT can use scripts/archive-source.sh" - echo "if they need to create valid source archives." - fi - echo - exit 1 - fi ;; *) echo "ERROR: invalid --with-git-submodules= value '$git_submodules_action'" @@ -1142,11 +1120,6 @@ if test "$tcg" = "auto"; then fi fi -if test "$tcg" = "enabled"; then - git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" - git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" -fi - ########################################## # big/little endian test cat > $TMPC << EOF diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index b99cb66e41..4899630491 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -26,8 +26,7 @@ sub_file="${sub_tdir}/submodule.tar" # independent of what the developer currently has initialized # in their checkout, because the build environment is completely # different to the host OS. -subprojects="dtc keycodemapdb libvfio-user" -submodules="tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" +subprojects="dtc keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3" sub_deinit="" function cleanup() { @@ -51,25 +50,6 @@ function tree_ish() { git archive --format tar "$(tree_ish)" > "$tar_file" test $? -ne 0 && error "failed to archive qemu" -for sm in $submodules; do - status="$(git submodule status "$sm")" - smhash="${status#[ +-]}" - smhash="${smhash%% *}" - case "$status" in - -*) - sub_deinit="$sub_deinit $sm" - git submodule update --init "$sm" - test $? -ne 0 && error "failed to update submodule $sm" - ;; - +*) - echo "WARNING: submodule $sm is out of sync" - ;; - esac - (cd $sm; git archive --format tar --prefix "$sm/" $(tree_ish)) > "$sub_file" - test $? -ne 0 && error "failed to archive submodule $sm ($smhash)" - tar --concatenate --file "$tar_file" "$sub_file" - test $? -ne 0 && error "failed append submodule $sm to $tar_file" -done for sp in $subprojects; do meson subprojects download $sp diff --git a/scripts/make-release b/scripts/make-release index 0604e61b81..c5db87b3f9 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -17,7 +17,7 @@ if [ $# -ne 2 ]; then fi # Only include wraps that are invoked with subproject() -SUBPROJECTS="dtc libvfio-user keycodemapdb" +SUBPROJECTS="dtc libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3" src="$1" version="$2" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 935e9a72a8..adca0266be 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -1,5 +1,7 @@ /packagecache +/berkeley-softfloat-3 +/berkeley-testfloat-3 /dtc /keycodemapdb /libvfio-user diff --git a/subprojects/berkeley-softfloat-3.wrap b/subprojects/berkeley-softfloat-3.wrap new file mode 100644 index 0000000000..a8fd87740b --- /dev/null +++ b/subprojects/berkeley-softfloat-3.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/berkeley-softfloat-3 +revision = b64af41c3276f97f0e181920400ee056b9c88037 +patch_directory = berkeley-softfloat-3 +depth = 1 diff --git a/subprojects/berkeley-testfloat-3.wrap b/subprojects/berkeley-testfloat-3.wrap new file mode 100644 index 0000000000..6ad80a37b2 --- /dev/null +++ b/subprojects/berkeley-testfloat-3.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/berkeley-testfloat-3 +revision = 40619cbb3bf32872df8c53cc457039229428a263 +patch_directory = berkeley-testfloat-3 +depth = 1 diff --git a/subprojects/packagefiles/berkeley-softfloat-3/meson.build b/subprojects/packagefiles/berkeley-softfloat-3/meson.build new file mode 100644 index 0000000000..4ce964b838 --- /dev/null +++ b/subprojects/packagefiles/berkeley-softfloat-3/meson.build @@ -0,0 +1,339 @@ +project('berkeley-softfloat-3', 'c', + default_options: ['warning_level=1', 'c_std=gnu99']) + +fpcflags = get_option('defines') + +platform_data = configuration_data() +platform_data.set('INLINE', 'static inline') +platform_data.set('LITTLEENDIAN', host_machine.endian() == 'little') +configure_file(output: 'platform.h', configuration: platform_data) + +sfdir = 'source' +sfspedir = sfdir / '8086-SSE' +sfinc = include_directories('.', sfdir / 'include', sfspedir) + +add_project_arguments([ + '-Wno-implicit-fallthrough', + '-Wno-missing-prototypes', + '-Wno-redundant-decls', + '-Wno-return-type', + '-Wno-error', +], native: false, language: 'c') + +libsoftfloat = static_library( + 'softfloat', + files( + # primitives + sfdir / 's_eq128.c', + sfdir / 's_le128.c', + sfdir / 's_lt128.c', + sfdir / 's_shortShiftLeft128.c', + sfdir / 's_shortShiftRight128.c', + sfdir / 's_shortShiftRightJam64.c', + sfdir / 's_shortShiftRightJam64Extra.c', + sfdir / 's_shortShiftRightJam128.c', + sfdir / 's_shortShiftRightJam128Extra.c', + sfdir / 's_shiftRightJam32.c', + sfdir / 's_shiftRightJam64.c', + sfdir / 's_shiftRightJam64Extra.c', + sfdir / 's_shiftRightJam128.c', + sfdir / 's_shiftRightJam128Extra.c', + sfdir / 's_shiftRightJam256M.c', + sfdir / 's_countLeadingZeros8.c', + sfdir / 's_countLeadingZeros16.c', + sfdir / 's_countLeadingZeros32.c', + sfdir / 's_countLeadingZeros64.c', + sfdir / 's_add128.c', + sfdir / 's_add256M.c', + sfdir / 's_sub128.c', + sfdir / 's_sub256M.c', + sfdir / 's_mul64ByShifted32To128.c', + sfdir / 's_mul64To128.c', + sfdir / 's_mul128By32.c', + sfdir / 's_mul128To256M.c', + sfdir / 's_approxRecip_1Ks.c', + sfdir / 's_approxRecip32_1.c', + sfdir / 's_approxRecipSqrt_1Ks.c', + sfdir / 's_approxRecipSqrt32_1.c', + # others + sfdir / 's_roundToUI32.c', + sfdir / 's_roundToUI64.c', + sfdir / 's_roundToI32.c', + sfdir / 's_roundToI64.c', + sfdir / 's_normSubnormalF16Sig.c', + sfdir / 's_roundPackToF16.c', + sfdir / 's_normRoundPackToF16.c', + sfdir / 's_addMagsF16.c', + sfdir / 's_subMagsF16.c', + sfdir / 's_mulAddF16.c', + sfdir / 's_normSubnormalF32Sig.c', + sfdir / 's_roundPackToF32.c', + sfdir / 's_normRoundPackToF32.c', + sfdir / 's_addMagsF32.c', + sfdir / 's_subMagsF32.c', + sfdir / 's_mulAddF32.c', + sfdir / 's_normSubnormalF64Sig.c', + sfdir / 's_roundPackToF64.c', + sfdir / 's_normRoundPackToF64.c', + sfdir / 's_addMagsF64.c', + sfdir / 's_subMagsF64.c', + sfdir / 's_mulAddF64.c', + sfdir / 's_normSubnormalExtF80Sig.c', + sfdir / 's_roundPackToExtF80.c', + sfdir / 's_normRoundPackToExtF80.c', + sfdir / 's_addMagsExtF80.c', + sfdir / 's_subMagsExtF80.c', + sfdir / 's_normSubnormalF128Sig.c', + sfdir / 's_roundPackToF128.c', + sfdir / 's_normRoundPackToF128.c', + sfdir / 's_addMagsF128.c', + sfdir / 's_subMagsF128.c', + sfdir / 's_mulAddF128.c', + sfdir / 'softfloat_state.c', + sfdir / 'ui32_to_f16.c', + sfdir / 'ui32_to_f32.c', + sfdir / 'ui32_to_f64.c', + sfdir / 'ui32_to_extF80.c', + sfdir / 'ui32_to_extF80M.c', + sfdir / 'ui32_to_f128.c', + sfdir / 'ui32_to_f128M.c', + sfdir / 'ui64_to_f16.c', + sfdir / 'ui64_to_f32.c', + sfdir / 'ui64_to_f64.c', + sfdir / 'ui64_to_extF80.c', + sfdir / 'ui64_to_extF80M.c', + sfdir / 'ui64_to_f128.c', + sfdir / 'ui64_to_f128M.c', + sfdir / 'i32_to_f16.c', + sfdir / 'i32_to_f32.c', + sfdir / 'i32_to_f64.c', + sfdir / 'i32_to_extF80.c', + sfdir / 'i32_to_extF80M.c', + sfdir / 'i32_to_f128.c', + sfdir / 'i32_to_f128M.c', + sfdir / 'i64_to_f16.c', + sfdir / 'i64_to_f32.c', + sfdir / 'i64_to_f64.c', + sfdir / 'i64_to_extF80.c', + sfdir / 'i64_to_extF80M.c', + sfdir / 'i64_to_f128.c', + sfdir / 'i64_to_f128M.c', + sfdir / 'f16_to_ui32.c', + sfdir / 'f16_to_ui64.c', + sfdir / 'f16_to_i32.c', + sfdir / 'f16_to_i64.c', + sfdir / 'f16_to_ui32_r_minMag.c', + sfdir / 'f16_to_ui64_r_minMag.c', + sfdir / 'f16_to_i32_r_minMag.c', + sfdir / 'f16_to_i64_r_minMag.c', + sfdir / 'f16_to_f32.c', + sfdir / 'f16_to_f64.c', + sfdir / 'f16_to_extF80.c', + sfdir / 'f16_to_extF80M.c', + sfdir / 'f16_to_f128.c', + sfdir / 'f16_to_f128M.c', + sfdir / 'f16_roundToInt.c', + sfdir / 'f16_add.c', + sfdir / 'f16_sub.c', + sfdir / 'f16_mul.c', + sfdir / 'f16_mulAdd.c', + sfdir / 'f16_div.c', + sfdir / 'f16_rem.c', + sfdir / 'f16_sqrt.c', + sfdir / 'f16_eq.c', + sfdir / 'f16_le.c', + sfdir / 'f16_lt.c', + sfdir / 'f16_eq_signaling.c', + sfdir / 'f16_le_quiet.c', + sfdir / 'f16_lt_quiet.c', + sfdir / 'f16_isSignalingNaN.c', + sfdir / 'f32_to_ui32.c', + sfdir / 'f32_to_ui64.c', + sfdir / 'f32_to_i32.c', + sfdir / 'f32_to_i64.c', + sfdir / 'f32_to_ui32_r_minMag.c', + sfdir / 'f32_to_ui64_r_minMag.c', + sfdir / 'f32_to_i32_r_minMag.c', + sfdir / 'f32_to_i64_r_minMag.c', + sfdir / 'f32_to_f16.c', + sfdir / 'f32_to_f64.c', + sfdir / 'f32_to_extF80.c', + sfdir / 'f32_to_extF80M.c', + sfdir / 'f32_to_f128.c', + sfdir / 'f32_to_f128M.c', + sfdir / 'f32_roundToInt.c', + sfdir / 'f32_add.c', + sfdir / 'f32_sub.c', + sfdir / 'f32_mul.c', + sfdir / 'f32_mulAdd.c', + sfdir / 'f32_div.c', + sfdir / 'f32_rem.c', + sfdir / 'f32_sqrt.c', + sfdir / 'f32_eq.c', + sfdir / 'f32_le.c', + sfdir / 'f32_lt.c', + sfdir / 'f32_eq_signaling.c', + sfdir / 'f32_le_quiet.c', + sfdir / 'f32_lt_quiet.c', + sfdir / 'f32_isSignalingNaN.c', + sfdir / 'f64_to_ui32.c', + sfdir / 'f64_to_ui64.c', + sfdir / 'f64_to_i32.c', + sfdir / 'f64_to_i64.c', + sfdir / 'f64_to_ui32_r_minMag.c', + sfdir / 'f64_to_ui64_r_minMag.c', + sfdir / 'f64_to_i32_r_minMag.c', + sfdir / 'f64_to_i64_r_minMag.c', + sfdir / 'f64_to_f16.c', + sfdir / 'f64_to_f32.c', + sfdir / 'f64_to_extF80.c', + sfdir / 'f64_to_extF80M.c', + sfdir / 'f64_to_f128.c', + sfdir / 'f64_to_f128M.c', + sfdir / 'f64_roundToInt.c', + sfdir / 'f64_add.c', + sfdir / 'f64_sub.c', + sfdir / 'f64_mul.c', + sfdir / 'f64_mulAdd.c', + sfdir / 'f64_div.c', + sfdir / 'f64_rem.c', + sfdir / 'f64_sqrt.c', + sfdir / 'f64_eq.c', + sfdir / 'f64_le.c', + sfdir / 'f64_lt.c', + sfdir / 'f64_eq_signaling.c', + sfdir / 'f64_le_quiet.c', + sfdir / 'f64_lt_quiet.c', + sfdir / 'f64_isSignalingNaN.c', + sfdir / 'extF80_to_ui32.c', + sfdir / 'extF80_to_ui64.c', + sfdir / 'extF80_to_i32.c', + sfdir / 'extF80_to_i64.c', + sfdir / 'extF80_to_ui32_r_minMag.c', + sfdir / 'extF80_to_ui64_r_minMag.c', + sfdir / 'extF80_to_i32_r_minMag.c', + sfdir / 'extF80_to_i64_r_minMag.c', + sfdir / 'extF80_to_f16.c', + sfdir / 'extF80_to_f32.c', + sfdir / 'extF80_to_f64.c', + sfdir / 'extF80_to_f128.c', + sfdir / 'extF80_roundToInt.c', + sfdir / 'extF80_add.c', + sfdir / 'extF80_sub.c', + sfdir / 'extF80_mul.c', + sfdir / 'extF80_div.c', + sfdir / 'extF80_rem.c', + sfdir / 'extF80_sqrt.c', + sfdir / 'extF80_eq.c', + sfdir / 'extF80_le.c', + sfdir / 'extF80_lt.c', + sfdir / 'extF80_eq_signaling.c', + sfdir / 'extF80_le_quiet.c', + sfdir / 'extF80_lt_quiet.c', + sfdir / 'extF80_isSignalingNaN.c', + sfdir / 'extF80M_to_ui32.c', + sfdir / 'extF80M_to_ui64.c', + sfdir / 'extF80M_to_i32.c', + sfdir / 'extF80M_to_i64.c', + sfdir / 'extF80M_to_ui32_r_minMag.c', + sfdir / 'extF80M_to_ui64_r_minMag.c', + sfdir / 'extF80M_to_i32_r_minMag.c', + sfdir / 'extF80M_to_i64_r_minMag.c', + sfdir / 'extF80M_to_f16.c', + sfdir / 'extF80M_to_f32.c', + sfdir / 'extF80M_to_f64.c', + sfdir / 'extF80M_to_f128M.c', + sfdir / 'extF80M_roundToInt.c', + sfdir / 'extF80M_add.c', + sfdir / 'extF80M_sub.c', + sfdir / 'extF80M_mul.c', + sfdir / 'extF80M_div.c', + sfdir / 'extF80M_rem.c', + sfdir / 'extF80M_sqrt.c', + sfdir / 'extF80M_eq.c', + sfdir / 'extF80M_le.c', + sfdir / 'extF80M_lt.c', + sfdir / 'extF80M_eq_signaling.c', + sfdir / 'extF80M_le_quiet.c', + sfdir / 'extF80M_lt_quiet.c', + sfdir / 'f128_to_ui32.c', + sfdir / 'f128_to_ui64.c', + sfdir / 'f128_to_i32.c', + sfdir / 'f128_to_i64.c', + sfdir / 'f128_to_ui32_r_minMag.c', + sfdir / 'f128_to_ui64_r_minMag.c', + sfdir / 'f128_to_i32_r_minMag.c', + sfdir / 'f128_to_i64_r_minMag.c', + sfdir / 'f128_to_f16.c', + sfdir / 'f128_to_f32.c', + sfdir / 'f128_to_extF80.c', + sfdir / 'f128_to_f64.c', + sfdir / 'f128_roundToInt.c', + sfdir / 'f128_add.c', + sfdir / 'f128_sub.c', + sfdir / 'f128_mul.c', + sfdir / 'f128_mulAdd.c', + sfdir / 'f128_div.c', + sfdir / 'f128_rem.c', + sfdir / 'f128_sqrt.c', + sfdir / 'f128_eq.c', + sfdir / 'f128_le.c', + sfdir / 'f128_lt.c', + sfdir / 'f128_eq_signaling.c', + sfdir / 'f128_le_quiet.c', + sfdir / 'f128_lt_quiet.c', + sfdir / 'f128_isSignalingNaN.c', + sfdir / 'f128M_to_ui32.c', + sfdir / 'f128M_to_ui64.c', + sfdir / 'f128M_to_i32.c', + sfdir / 'f128M_to_i64.c', + sfdir / 'f128M_to_ui32_r_minMag.c', + sfdir / 'f128M_to_ui64_r_minMag.c', + sfdir / 'f128M_to_i32_r_minMag.c', + sfdir / 'f128M_to_i64_r_minMag.c', + sfdir / 'f128M_to_f16.c', + sfdir / 'f128M_to_f32.c', + sfdir / 'f128M_to_extF80M.c', + sfdir / 'f128M_to_f64.c', + sfdir / 'f128M_roundToInt.c', + sfdir / 'f128M_add.c', + sfdir / 'f128M_sub.c', + sfdir / 'f128M_mul.c', + sfdir / 'f128M_mulAdd.c', + sfdir / 'f128M_div.c', + sfdir / 'f128M_rem.c', + sfdir / 'f128M_sqrt.c', + sfdir / 'f128M_eq.c', + sfdir / 'f128M_le.c', + sfdir / 'f128M_lt.c', + sfdir / 'f128M_eq_signaling.c', + sfdir / 'f128M_le_quiet.c', + sfdir / 'f128M_lt_quiet.c', + # spe + sfspedir / 'softfloat_raiseFlags.c', + sfspedir / 's_f16UIToCommonNaN.c', + sfspedir / 's_commonNaNToF16UI.c', + sfspedir / 's_propagateNaNF16UI.c', + sfspedir / 's_f32UIToCommonNaN.c', + sfspedir / 's_commonNaNToF32UI.c', + sfspedir / 's_propagateNaNF32UI.c', + sfspedir / 's_f64UIToCommonNaN.c', + sfspedir / 's_commonNaNToF64UI.c', + sfspedir / 's_propagateNaNF64UI.c', + sfspedir / 'extF80M_isSignalingNaN.c', + sfspedir / 's_extF80UIToCommonNaN.c', + sfspedir / 's_commonNaNToExtF80UI.c', + sfspedir / 's_propagateNaNExtF80UI.c', + sfspedir / 'f128M_isSignalingNaN.c', + sfspedir / 's_f128UIToCommonNaN.c', + sfspedir / 's_commonNaNToF128UI.c', + sfspedir / 's_propagateNaNF128UI.c', + ), + include_directories: sfinc, + c_args: fpcflags, +) + +libsoftfloat_dep = declare_dependency( + link_with: libsoftfloat, + include_directories: sfinc, + compile_args: fpcflags) diff --git a/subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt b/subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt new file mode 100644 index 0000000000..868ae57e80 --- /dev/null +++ b/subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt @@ -0,0 +1 @@ +option('defines', type : 'array', value : []) diff --git a/subprojects/packagefiles/berkeley-testfloat-3/meson.build b/subprojects/packagefiles/berkeley-testfloat-3/meson.build new file mode 100644 index 0000000000..a41673d616 --- /dev/null +++ b/subprojects/packagefiles/berkeley-testfloat-3/meson.build @@ -0,0 +1,220 @@ +project('berkeley-testfloat-3', 'c', + default_options: ['warning_level=1', 'c_std=gnu99']) + +fpcflags = get_option('defines') + +platform_data = configuration_data() +platform_data.set('INLINE', 'static inline') +platform_data.set('LITTLEENDIAN', host_machine.endian() == 'little') +configure_file(output: 'platform.h', configuration: platform_data) + +tfdir = 'source' +tfinc = include_directories('.', tfdir) + +add_project_arguments( + [ + '-Wno-implicit-fallthrough', + '-Wno-strict-prototypes', + '-Wno-unknown-pragmas', + '-Wno-uninitialized', + '-Wno-missing-prototypes', + '-Wno-return-type', + '-Wno-unused-function', + '-Wno-missing-format-attribute', + '-Wno-error', + ] + meson.get_compiler('c').get_supported_arguments('-Wno-ignored-pragmas'), + native: false, language: 'c') + +tfgencases = [ + tfdir / 'genCases_ui32.c', + tfdir / 'genCases_ui64.c', + tfdir / 'genCases_i32.c', + tfdir / 'genCases_i64.c', + tfdir / 'genCases_f16.c', + tfdir / 'genCases_f32.c', + tfdir / 'genCases_f64.c', + tfdir / 'genCases_extF80.c', + tfdir / 'genCases_f128.c', +] + +tfwritecase = [ + tfdir / 'writeCase_a_ui32.c', + tfdir / 'writeCase_a_ui64.c', + tfdir / 'writeCase_a_f16.c', + tfdir / 'writeCase_ab_f16.c', + tfdir / 'writeCase_abc_f16.c', + tfdir / 'writeCase_a_f32.c', + tfdir / 'writeCase_ab_f32.c', + tfdir / 'writeCase_abc_f32.c', + tfdir / 'writeCase_a_f64.c', + tfdir / 'writeCase_ab_f64.c', + tfdir / 'writeCase_abc_f64.c', + tfdir / 'writeCase_a_extF80M.c', + tfdir / 'writeCase_ab_extF80M.c', + tfdir / 'writeCase_a_f128M.c', + tfdir / 'writeCase_ab_f128M.c', + tfdir / 'writeCase_abc_f128M.c', + tfdir / 'writeCase_z_bool.c', + tfdir / 'writeCase_z_ui32.c', + tfdir / 'writeCase_z_ui64.c', + tfdir / 'writeCase_z_f16.c', + tfdir / 'writeCase_z_f32.c', + tfdir / 'writeCase_z_f64.c', + tfdir / 'writeCase_z_extF80M.c', + tfdir / 'writeCase_z_f128M.c', +] + +tftest = [ + tfdir / 'test_a_ui32_z_f16.c', + tfdir / 'test_a_ui32_z_f32.c', + tfdir / 'test_a_ui32_z_f64.c', + tfdir / 'test_a_ui32_z_extF80.c', + tfdir / 'test_a_ui32_z_f128.c', + tfdir / 'test_a_ui64_z_f16.c', + tfdir / 'test_a_ui64_z_f32.c', + tfdir / 'test_a_ui64_z_f64.c', + tfdir / 'test_a_ui64_z_extF80.c', + tfdir / 'test_a_ui64_z_f128.c', + tfdir / 'test_a_i32_z_f16.c', + tfdir / 'test_a_i32_z_f32.c', + tfdir / 'test_a_i32_z_f64.c', + tfdir / 'test_a_i32_z_extF80.c', + tfdir / 'test_a_i32_z_f128.c', + tfdir / 'test_a_i64_z_f16.c', + tfdir / 'test_a_i64_z_f32.c', + tfdir / 'test_a_i64_z_f64.c', + tfdir / 'test_a_i64_z_extF80.c', + tfdir / 'test_a_i64_z_f128.c', + tfdir / 'test_a_f16_z_ui32_rx.c', + tfdir / 'test_a_f16_z_ui64_rx.c', + tfdir / 'test_a_f16_z_i32_rx.c', + tfdir / 'test_a_f16_z_i64_rx.c', + tfdir / 'test_a_f16_z_ui32_x.c', + tfdir / 'test_a_f16_z_ui64_x.c', + tfdir / 'test_a_f16_z_i32_x.c', + tfdir / 'test_a_f16_z_i64_x.c', + tfdir / 'test_a_f16_z_f32.c', + tfdir / 'test_a_f16_z_f64.c', + tfdir / 'test_a_f16_z_extF80.c', + tfdir / 'test_a_f16_z_f128.c', + tfdir / 'test_az_f16.c', + tfdir / 'test_az_f16_rx.c', + tfdir / 'test_abz_f16.c', + tfdir / 'test_abcz_f16.c', + tfdir / 'test_ab_f16_z_bool.c', + tfdir / 'test_a_f32_z_ui32_rx.c', + tfdir / 'test_a_f32_z_ui64_rx.c', + tfdir / 'test_a_f32_z_i32_rx.c', + tfdir / 'test_a_f32_z_i64_rx.c', + tfdir / 'test_a_f32_z_ui32_x.c', + tfdir / 'test_a_f32_z_ui64_x.c', + tfdir / 'test_a_f32_z_i32_x.c', + tfdir / 'test_a_f32_z_i64_x.c', + tfdir / 'test_a_f32_z_f16.c', + tfdir / 'test_a_f32_z_f64.c', + tfdir / 'test_a_f32_z_extF80.c', + tfdir / 'test_a_f32_z_f128.c', + tfdir / 'test_az_f32.c', + tfdir / 'test_az_f32_rx.c', + tfdir / 'test_abz_f32.c', + tfdir / 'test_abcz_f32.c', + tfdir / 'test_ab_f32_z_bool.c', + tfdir / 'test_a_f64_z_ui32_rx.c', + tfdir / 'test_a_f64_z_ui64_rx.c', + tfdir / 'test_a_f64_z_i32_rx.c', + tfdir / 'test_a_f64_z_i64_rx.c', + tfdir / 'test_a_f64_z_ui32_x.c', + tfdir / 'test_a_f64_z_ui64_x.c', + tfdir / 'test_a_f64_z_i32_x.c', + tfdir / 'test_a_f64_z_i64_x.c', + tfdir / 'test_a_f64_z_f16.c', + tfdir / 'test_a_f64_z_f32.c', + tfdir / 'test_a_f64_z_extF80.c', + tfdir / 'test_a_f64_z_f128.c', + tfdir / 'test_az_f64.c', + tfdir / 'test_az_f64_rx.c', + tfdir / 'test_abz_f64.c', + tfdir / 'test_abcz_f64.c', + tfdir / 'test_ab_f64_z_bool.c', + tfdir / 'test_a_extF80_z_ui32_rx.c', + tfdir / 'test_a_extF80_z_ui64_rx.c', + tfdir / 'test_a_extF80_z_i32_rx.c', + tfdir / 'test_a_extF80_z_i64_rx.c', + tfdir / 'test_a_extF80_z_ui32_x.c', + tfdir / 'test_a_extF80_z_ui64_x.c', + tfdir / 'test_a_extF80_z_i32_x.c', + tfdir / 'test_a_extF80_z_i64_x.c', + tfdir / 'test_a_extF80_z_f16.c', + tfdir / 'test_a_extF80_z_f32.c', + tfdir / 'test_a_extF80_z_f64.c', + tfdir / 'test_a_extF80_z_f128.c', + tfdir / 'test_az_extF80.c', + tfdir / 'test_az_extF80_rx.c', + tfdir / 'test_abz_extF80.c', + tfdir / 'test_ab_extF80_z_bool.c', + tfdir / 'test_a_f128_z_ui32_rx.c', + tfdir / 'test_a_f128_z_ui64_rx.c', + tfdir / 'test_a_f128_z_i32_rx.c', + tfdir / 'test_a_f128_z_i64_rx.c', + tfdir / 'test_a_f128_z_ui32_x.c', + tfdir / 'test_a_f128_z_ui64_x.c', + tfdir / 'test_a_f128_z_i32_x.c', + tfdir / 'test_a_f128_z_i64_x.c', + tfdir / 'test_a_f128_z_f16.c', + tfdir / 'test_a_f128_z_f32.c', + tfdir / 'test_a_f128_z_f64.c', + tfdir / 'test_a_f128_z_extF80.c', + tfdir / 'test_az_f128.c', + tfdir / 'test_az_f128_rx.c', + tfdir / 'test_abz_f128.c', + tfdir / 'test_abcz_f128.c', + tfdir / 'test_ab_f128_z_bool.c', +] + +libsoftfloat_proj = subproject('berkeley-softfloat-3', required: true) +libsoftfloat = libsoftfloat_proj.get_variable('libsoftfloat_dep') + +libtestfloat = static_library( + 'testfloat', + files( + tfdir / 'uint128_inline.c', + tfdir / 'uint128.c', + tfdir / 'fail.c', + tfdir / 'functions_common.c', + tfdir / 'functionInfos.c', + tfdir / 'standardFunctionInfos.c', + tfdir / 'random.c', + tfdir / 'genCases_common.c', + tfgencases, + tfdir / 'genCases_writeTestsTotal.c', + tfdir / 'verCases_inline.c', + tfdir / 'verCases_common.c', + tfdir / 'verCases_writeFunctionName.c', + tfdir / 'readHex.c', + tfdir / 'writeHex.c', + tfwritecase, + tfdir / 'testLoops_common.c', + tftest, + ), + dependencies: libsoftfloat.partial_dependency(includes: true, compile_args: true), + c_args: fpcflags, +) + +libtestfloat_dep = declare_dependency( + link_with: libtestfloat, + dependencies: libsoftfloat, + include_directories: tfinc, + compile_args: fpcflags) + +libslowfloat = static_library( + 'slowfloat', + tfdir / 'slowfloat.c', + dependencies: libsoftfloat.partial_dependency(includes: true, compile_args: true), + c_args: fpcflags, +) + +libslowfloat_dep = declare_dependency( + link_with: libslowfloat, + dependencies: libsoftfloat, + include_directories: tfinc, + compile_args: fpcflags) diff --git a/subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt b/subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt new file mode 100644 index 0000000000..868ae57e80 --- /dev/null +++ b/subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt @@ -0,0 +1 @@ +option('defines', type : 'array', value : []) diff --git a/tests/fp/meson.build b/tests/fp/meson.build index f9ca6a93b4..cbc17392d6 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -7,13 +7,15 @@ if targetos == 'windows' subdir_done() endif -fpcflags = [ +sfcflags = [ # softfloat defines '-DSOFTFLOAT_ROUND_ODD', '-DINLINE_LEVEL=5', '-DSOFTFLOAT_FAST_DIV32TO16', '-DSOFTFLOAT_FAST_DIV64TO32', '-DSOFTFLOAT_FAST_INT64', +] +tfcflags = [ # testfloat defines '-DFLOAT16', '-DFLOAT64', @@ -23,523 +25,16 @@ fpcflags = [ '-DLONG_DOUBLE_IS_EXTFLOAT80', ] -sfdir = 'berkeley-softfloat-3/source' -sfspedir = sfdir / '8086-SSE' -tfdir = 'berkeley-testfloat-3/source' +libsoftfloat_proj = subproject('berkeley-softfloat-3', required: true, + default_options: 'defines=' + ','.join(sfcflags)) +libsoftfloat = libsoftfloat_proj.get_variable('libsoftfloat_dep') -sfinc = include_directories(sfdir / 'include', sfspedir) +libtestfloat_proj = subproject('berkeley-testfloat-3', required: true, + default_options: 'defines=' + ','.join(tfcflags)) +libtestfloat = libtestfloat_proj.get_variable('libtestfloat_dep') +libslowfloat = libtestfloat_proj.get_variable('libslowfloat_dep') -tfcflags = [ - '-Wno-implicit-fallthrough', - '-Wno-strict-prototypes', - '-Wno-unknown-pragmas', - '-Wno-uninitialized', - '-Wno-missing-prototypes', - '-Wno-return-type', - '-Wno-unused-function', - '-Wno-missing-format-attribute', - '-Wno-error', -] - -if cc.get_id() == 'clang' - # Clang does not support '#pragma STDC FENV_ACCESS' - tfcflags += [ '-Wno-ignored-pragmas' ] -endif - -tfgencases = [ - tfdir / 'genCases_ui32.c', - tfdir / 'genCases_ui64.c', - tfdir / 'genCases_i32.c', - tfdir / 'genCases_i64.c', - tfdir / 'genCases_f16.c', - tfdir / 'genCases_f32.c', - tfdir / 'genCases_f64.c', - tfdir / 'genCases_extF80.c', - tfdir / 'genCases_f128.c', -] - -tfwritecase = [ - tfdir / 'writeCase_a_ui32.c', - tfdir / 'writeCase_a_ui64.c', - tfdir / 'writeCase_a_f16.c', - tfdir / 'writeCase_ab_f16.c', - tfdir / 'writeCase_abc_f16.c', - tfdir / 'writeCase_a_f32.c', - tfdir / 'writeCase_ab_f32.c', - tfdir / 'writeCase_abc_f32.c', - tfdir / 'writeCase_a_f64.c', - tfdir / 'writeCase_ab_f64.c', - tfdir / 'writeCase_abc_f64.c', - tfdir / 'writeCase_a_extF80M.c', - tfdir / 'writeCase_ab_extF80M.c', - tfdir / 'writeCase_a_f128M.c', - tfdir / 'writeCase_ab_f128M.c', - tfdir / 'writeCase_abc_f128M.c', - tfdir / 'writeCase_z_bool.c', - tfdir / 'writeCase_z_ui32.c', - tfdir / 'writeCase_z_ui64.c', - tfdir / 'writeCase_z_f16.c', - tfdir / 'writeCase_z_f32.c', - tfdir / 'writeCase_z_f64.c', - tfdir / 'writeCase_z_extF80M.c', - tfdir / 'writeCase_z_f128M.c', -] - -tftest = [ - tfdir / 'test_a_ui32_z_f16.c', - tfdir / 'test_a_ui32_z_f32.c', - tfdir / 'test_a_ui32_z_f64.c', - tfdir / 'test_a_ui32_z_extF80.c', - tfdir / 'test_a_ui32_z_f128.c', - tfdir / 'test_a_ui64_z_f16.c', - tfdir / 'test_a_ui64_z_f32.c', - tfdir / 'test_a_ui64_z_f64.c', - tfdir / 'test_a_ui64_z_extF80.c', - tfdir / 'test_a_ui64_z_f128.c', - tfdir / 'test_a_i32_z_f16.c', - tfdir / 'test_a_i32_z_f32.c', - tfdir / 'test_a_i32_z_f64.c', - tfdir / 'test_a_i32_z_extF80.c', - tfdir / 'test_a_i32_z_f128.c', - tfdir / 'test_a_i64_z_f16.c', - tfdir / 'test_a_i64_z_f32.c', - tfdir / 'test_a_i64_z_f64.c', - tfdir / 'test_a_i64_z_extF80.c', - tfdir / 'test_a_i64_z_f128.c', - tfdir / 'test_a_f16_z_ui32_rx.c', - tfdir / 'test_a_f16_z_ui64_rx.c', - tfdir / 'test_a_f16_z_i32_rx.c', - tfdir / 'test_a_f16_z_i64_rx.c', - tfdir / 'test_a_f16_z_ui32_x.c', - tfdir / 'test_a_f16_z_ui64_x.c', - tfdir / 'test_a_f16_z_i32_x.c', - tfdir / 'test_a_f16_z_i64_x.c', - tfdir / 'test_a_f16_z_f32.c', - tfdir / 'test_a_f16_z_f64.c', - tfdir / 'test_a_f16_z_extF80.c', - tfdir / 'test_a_f16_z_f128.c', - tfdir / 'test_az_f16.c', - tfdir / 'test_az_f16_rx.c', - tfdir / 'test_abz_f16.c', - tfdir / 'test_abcz_f16.c', - tfdir / 'test_ab_f16_z_bool.c', - tfdir / 'test_a_f32_z_ui32_rx.c', - tfdir / 'test_a_f32_z_ui64_rx.c', - tfdir / 'test_a_f32_z_i32_rx.c', - tfdir / 'test_a_f32_z_i64_rx.c', - tfdir / 'test_a_f32_z_ui32_x.c', - tfdir / 'test_a_f32_z_ui64_x.c', - tfdir / 'test_a_f32_z_i32_x.c', - tfdir / 'test_a_f32_z_i64_x.c', - tfdir / 'test_a_f32_z_f16.c', - tfdir / 'test_a_f32_z_f64.c', - tfdir / 'test_a_f32_z_extF80.c', - tfdir / 'test_a_f32_z_f128.c', - tfdir / 'test_az_f32.c', - tfdir / 'test_az_f32_rx.c', - tfdir / 'test_abz_f32.c', - tfdir / 'test_abcz_f32.c', - tfdir / 'test_ab_f32_z_bool.c', - tfdir / 'test_a_f64_z_ui32_rx.c', - tfdir / 'test_a_f64_z_ui64_rx.c', - tfdir / 'test_a_f64_z_i32_rx.c', - tfdir / 'test_a_f64_z_i64_rx.c', - tfdir / 'test_a_f64_z_ui32_x.c', - tfdir / 'test_a_f64_z_ui64_x.c', - tfdir / 'test_a_f64_z_i32_x.c', - tfdir / 'test_a_f64_z_i64_x.c', - tfdir / 'test_a_f64_z_f16.c', - tfdir / 'test_a_f64_z_f32.c', - tfdir / 'test_a_f64_z_extF80.c', - tfdir / 'test_a_f64_z_f128.c', - tfdir / 'test_az_f64.c', - tfdir / 'test_az_f64_rx.c', - tfdir / 'test_abz_f64.c', - tfdir / 'test_abcz_f64.c', - tfdir / 'test_ab_f64_z_bool.c', - tfdir / 'test_a_extF80_z_ui32_rx.c', - tfdir / 'test_a_extF80_z_ui64_rx.c', - tfdir / 'test_a_extF80_z_i32_rx.c', - tfdir / 'test_a_extF80_z_i64_rx.c', - tfdir / 'test_a_extF80_z_ui32_x.c', - tfdir / 'test_a_extF80_z_ui64_x.c', - tfdir / 'test_a_extF80_z_i32_x.c', - tfdir / 'test_a_extF80_z_i64_x.c', - tfdir / 'test_a_extF80_z_f16.c', - tfdir / 'test_a_extF80_z_f32.c', - tfdir / 'test_a_extF80_z_f64.c', - tfdir / 'test_a_extF80_z_f128.c', - tfdir / 'test_az_extF80.c', - tfdir / 'test_az_extF80_rx.c', - tfdir / 'test_abz_extF80.c', - tfdir / 'test_ab_extF80_z_bool.c', - tfdir / 'test_a_f128_z_ui32_rx.c', - tfdir / 'test_a_f128_z_ui64_rx.c', - tfdir / 'test_a_f128_z_i32_rx.c', - tfdir / 'test_a_f128_z_i64_rx.c', - tfdir / 'test_a_f128_z_ui32_x.c', - tfdir / 'test_a_f128_z_ui64_x.c', - tfdir / 'test_a_f128_z_i32_x.c', - tfdir / 'test_a_f128_z_i64_x.c', - tfdir / 'test_a_f128_z_f16.c', - tfdir / 'test_a_f128_z_f32.c', - tfdir / 'test_a_f128_z_f64.c', - tfdir / 'test_a_f128_z_extF80.c', - tfdir / 'test_az_f128.c', - tfdir / 'test_az_f128_rx.c', - tfdir / 'test_abz_f128.c', - tfdir / 'test_abcz_f128.c', - tfdir / 'test_ab_f128_z_bool.c', -] - -libtestfloat = static_library( - 'testfloat', - files( - tfdir / 'uint128_inline.c', - tfdir / 'uint128.c', - tfdir / 'fail.c', - tfdir / 'functions_common.c', - tfdir / 'functionInfos.c', - tfdir / 'standardFunctionInfos.c', - tfdir / 'random.c', - tfdir / 'genCases_common.c', - tfgencases, - tfdir / 'genCases_writeTestsTotal.c', - tfdir / 'verCases_inline.c', - tfdir / 'verCases_common.c', - tfdir / 'verCases_writeFunctionName.c', - tfdir / 'readHex.c', - tfdir / 'writeHex.c', - tfwritecase, - tfdir / 'testLoops_common.c', - tftest, - ), - include_directories: sfinc, - c_args: tfcflags + fpcflags, -) - -sfcflags = [ - '-Wno-implicit-fallthrough', - '-Wno-missing-prototypes', - '-Wno-redundant-decls', - '-Wno-return-type', - '-Wno-error', -] - -libsoftfloat = static_library( - 'softfloat', - files( - # primitives - sfdir / 's_eq128.c', - sfdir / 's_le128.c', - sfdir / 's_lt128.c', - sfdir / 's_shortShiftLeft128.c', - sfdir / 's_shortShiftRight128.c', - sfdir / 's_shortShiftRightJam64.c', - sfdir / 's_shortShiftRightJam64Extra.c', - sfdir / 's_shortShiftRightJam128.c', - sfdir / 's_shortShiftRightJam128Extra.c', - sfdir / 's_shiftRightJam32.c', - sfdir / 's_shiftRightJam64.c', - sfdir / 's_shiftRightJam64Extra.c', - sfdir / 's_shiftRightJam128.c', - sfdir / 's_shiftRightJam128Extra.c', - sfdir / 's_shiftRightJam256M.c', - sfdir / 's_countLeadingZeros8.c', - sfdir / 's_countLeadingZeros16.c', - sfdir / 's_countLeadingZeros32.c', - sfdir / 's_countLeadingZeros64.c', - sfdir / 's_add128.c', - sfdir / 's_add256M.c', - sfdir / 's_sub128.c', - sfdir / 's_sub256M.c', - sfdir / 's_mul64ByShifted32To128.c', - sfdir / 's_mul64To128.c', - sfdir / 's_mul128By32.c', - sfdir / 's_mul128To256M.c', - sfdir / 's_approxRecip_1Ks.c', - sfdir / 's_approxRecip32_1.c', - sfdir / 's_approxRecipSqrt_1Ks.c', - sfdir / 's_approxRecipSqrt32_1.c', - # others - sfdir / 's_roundToUI32.c', - sfdir / 's_roundToUI64.c', - sfdir / 's_roundToI32.c', - sfdir / 's_roundToI64.c', - sfdir / 's_normSubnormalF16Sig.c', - sfdir / 's_roundPackToF16.c', - sfdir / 's_normRoundPackToF16.c', - sfdir / 's_addMagsF16.c', - sfdir / 's_subMagsF16.c', - sfdir / 's_mulAddF16.c', - sfdir / 's_normSubnormalF32Sig.c', - sfdir / 's_roundPackToF32.c', - sfdir / 's_normRoundPackToF32.c', - sfdir / 's_addMagsF32.c', - sfdir / 's_subMagsF32.c', - sfdir / 's_mulAddF32.c', - sfdir / 's_normSubnormalF64Sig.c', - sfdir / 's_roundPackToF64.c', - sfdir / 's_normRoundPackToF64.c', - sfdir / 's_addMagsF64.c', - sfdir / 's_subMagsF64.c', - sfdir / 's_mulAddF64.c', - sfdir / 's_normSubnormalExtF80Sig.c', - sfdir / 's_roundPackToExtF80.c', - sfdir / 's_normRoundPackToExtF80.c', - sfdir / 's_addMagsExtF80.c', - sfdir / 's_subMagsExtF80.c', - sfdir / 's_normSubnormalF128Sig.c', - sfdir / 's_roundPackToF128.c', - sfdir / 's_normRoundPackToF128.c', - sfdir / 's_addMagsF128.c', - sfdir / 's_subMagsF128.c', - sfdir / 's_mulAddF128.c', - sfdir / 'softfloat_state.c', - sfdir / 'ui32_to_f16.c', - sfdir / 'ui32_to_f32.c', - sfdir / 'ui32_to_f64.c', - sfdir / 'ui32_to_extF80.c', - sfdir / 'ui32_to_extF80M.c', - sfdir / 'ui32_to_f128.c', - sfdir / 'ui32_to_f128M.c', - sfdir / 'ui64_to_f16.c', - sfdir / 'ui64_to_f32.c', - sfdir / 'ui64_to_f64.c', - sfdir / 'ui64_to_extF80.c', - sfdir / 'ui64_to_extF80M.c', - sfdir / 'ui64_to_f128.c', - sfdir / 'ui64_to_f128M.c', - sfdir / 'i32_to_f16.c', - sfdir / 'i32_to_f32.c', - sfdir / 'i32_to_f64.c', - sfdir / 'i32_to_extF80.c', - sfdir / 'i32_to_extF80M.c', - sfdir / 'i32_to_f128.c', - sfdir / 'i32_to_f128M.c', - sfdir / 'i64_to_f16.c', - sfdir / 'i64_to_f32.c', - sfdir / 'i64_to_f64.c', - sfdir / 'i64_to_extF80.c', - sfdir / 'i64_to_extF80M.c', - sfdir / 'i64_to_f128.c', - sfdir / 'i64_to_f128M.c', - sfdir / 'f16_to_ui32.c', - sfdir / 'f16_to_ui64.c', - sfdir / 'f16_to_i32.c', - sfdir / 'f16_to_i64.c', - sfdir / 'f16_to_ui32_r_minMag.c', - sfdir / 'f16_to_ui64_r_minMag.c', - sfdir / 'f16_to_i32_r_minMag.c', - sfdir / 'f16_to_i64_r_minMag.c', - sfdir / 'f16_to_f32.c', - sfdir / 'f16_to_f64.c', - sfdir / 'f16_to_extF80.c', - sfdir / 'f16_to_extF80M.c', - sfdir / 'f16_to_f128.c', - sfdir / 'f16_to_f128M.c', - sfdir / 'f16_roundToInt.c', - sfdir / 'f16_add.c', - sfdir / 'f16_sub.c', - sfdir / 'f16_mul.c', - sfdir / 'f16_mulAdd.c', - sfdir / 'f16_div.c', - sfdir / 'f16_rem.c', - sfdir / 'f16_sqrt.c', - sfdir / 'f16_eq.c', - sfdir / 'f16_le.c', - sfdir / 'f16_lt.c', - sfdir / 'f16_eq_signaling.c', - sfdir / 'f16_le_quiet.c', - sfdir / 'f16_lt_quiet.c', - sfdir / 'f16_isSignalingNaN.c', - sfdir / 'f32_to_ui32.c', - sfdir / 'f32_to_ui64.c', - sfdir / 'f32_to_i32.c', - sfdir / 'f32_to_i64.c', - sfdir / 'f32_to_ui32_r_minMag.c', - sfdir / 'f32_to_ui64_r_minMag.c', - sfdir / 'f32_to_i32_r_minMag.c', - sfdir / 'f32_to_i64_r_minMag.c', - sfdir / 'f32_to_f16.c', - sfdir / 'f32_to_f64.c', - sfdir / 'f32_to_extF80.c', - sfdir / 'f32_to_extF80M.c', - sfdir / 'f32_to_f128.c', - sfdir / 'f32_to_f128M.c', - sfdir / 'f32_roundToInt.c', - sfdir / 'f32_add.c', - sfdir / 'f32_sub.c', - sfdir / 'f32_mul.c', - sfdir / 'f32_mulAdd.c', - sfdir / 'f32_div.c', - sfdir / 'f32_rem.c', - sfdir / 'f32_sqrt.c', - sfdir / 'f32_eq.c', - sfdir / 'f32_le.c', - sfdir / 'f32_lt.c', - sfdir / 'f32_eq_signaling.c', - sfdir / 'f32_le_quiet.c', - sfdir / 'f32_lt_quiet.c', - sfdir / 'f32_isSignalingNaN.c', - sfdir / 'f64_to_ui32.c', - sfdir / 'f64_to_ui64.c', - sfdir / 'f64_to_i32.c', - sfdir / 'f64_to_i64.c', - sfdir / 'f64_to_ui32_r_minMag.c', - sfdir / 'f64_to_ui64_r_minMag.c', - sfdir / 'f64_to_i32_r_minMag.c', - sfdir / 'f64_to_i64_r_minMag.c', - sfdir / 'f64_to_f16.c', - sfdir / 'f64_to_f32.c', - sfdir / 'f64_to_extF80.c', - sfdir / 'f64_to_extF80M.c', - sfdir / 'f64_to_f128.c', - sfdir / 'f64_to_f128M.c', - sfdir / 'f64_roundToInt.c', - sfdir / 'f64_add.c', - sfdir / 'f64_sub.c', - sfdir / 'f64_mul.c', - sfdir / 'f64_mulAdd.c', - sfdir / 'f64_div.c', - sfdir / 'f64_rem.c', - sfdir / 'f64_sqrt.c', - sfdir / 'f64_eq.c', - sfdir / 'f64_le.c', - sfdir / 'f64_lt.c', - sfdir / 'f64_eq_signaling.c', - sfdir / 'f64_le_quiet.c', - sfdir / 'f64_lt_quiet.c', - sfdir / 'f64_isSignalingNaN.c', - sfdir / 'extF80_to_ui32.c', - sfdir / 'extF80_to_ui64.c', - sfdir / 'extF80_to_i32.c', - sfdir / 'extF80_to_i64.c', - sfdir / 'extF80_to_ui32_r_minMag.c', - sfdir / 'extF80_to_ui64_r_minMag.c', - sfdir / 'extF80_to_i32_r_minMag.c', - sfdir / 'extF80_to_i64_r_minMag.c', - sfdir / 'extF80_to_f16.c', - sfdir / 'extF80_to_f32.c', - sfdir / 'extF80_to_f64.c', - sfdir / 'extF80_to_f128.c', - sfdir / 'extF80_roundToInt.c', - sfdir / 'extF80_add.c', - sfdir / 'extF80_sub.c', - sfdir / 'extF80_mul.c', - sfdir / 'extF80_div.c', - sfdir / 'extF80_rem.c', - sfdir / 'extF80_sqrt.c', - sfdir / 'extF80_eq.c', - sfdir / 'extF80_le.c', - sfdir / 'extF80_lt.c', - sfdir / 'extF80_eq_signaling.c', - sfdir / 'extF80_le_quiet.c', - sfdir / 'extF80_lt_quiet.c', - sfdir / 'extF80_isSignalingNaN.c', - sfdir / 'extF80M_to_ui32.c', - sfdir / 'extF80M_to_ui64.c', - sfdir / 'extF80M_to_i32.c', - sfdir / 'extF80M_to_i64.c', - sfdir / 'extF80M_to_ui32_r_minMag.c', - sfdir / 'extF80M_to_ui64_r_minMag.c', - sfdir / 'extF80M_to_i32_r_minMag.c', - sfdir / 'extF80M_to_i64_r_minMag.c', - sfdir / 'extF80M_to_f16.c', - sfdir / 'extF80M_to_f32.c', - sfdir / 'extF80M_to_f64.c', - sfdir / 'extF80M_to_f128M.c', - sfdir / 'extF80M_roundToInt.c', - sfdir / 'extF80M_add.c', - sfdir / 'extF80M_sub.c', - sfdir / 'extF80M_mul.c', - sfdir / 'extF80M_div.c', - sfdir / 'extF80M_rem.c', - sfdir / 'extF80M_sqrt.c', - sfdir / 'extF80M_eq.c', - sfdir / 'extF80M_le.c', - sfdir / 'extF80M_lt.c', - sfdir / 'extF80M_eq_signaling.c', - sfdir / 'extF80M_le_quiet.c', - sfdir / 'extF80M_lt_quiet.c', - sfdir / 'f128_to_ui32.c', - sfdir / 'f128_to_ui64.c', - sfdir / 'f128_to_i32.c', - sfdir / 'f128_to_i64.c', - sfdir / 'f128_to_ui32_r_minMag.c', - sfdir / 'f128_to_ui64_r_minMag.c', - sfdir / 'f128_to_i32_r_minMag.c', - sfdir / 'f128_to_i64_r_minMag.c', - sfdir / 'f128_to_f16.c', - sfdir / 'f128_to_f32.c', - sfdir / 'f128_to_extF80.c', - sfdir / 'f128_to_f64.c', - sfdir / 'f128_roundToInt.c', - sfdir / 'f128_add.c', - sfdir / 'f128_sub.c', - sfdir / 'f128_mul.c', - sfdir / 'f128_mulAdd.c', - sfdir / 'f128_div.c', - sfdir / 'f128_rem.c', - sfdir / 'f128_sqrt.c', - sfdir / 'f128_eq.c', - sfdir / 'f128_le.c', - sfdir / 'f128_lt.c', - sfdir / 'f128_eq_signaling.c', - sfdir / 'f128_le_quiet.c', - sfdir / 'f128_lt_quiet.c', - sfdir / 'f128_isSignalingNaN.c', - sfdir / 'f128M_to_ui32.c', - sfdir / 'f128M_to_ui64.c', - sfdir / 'f128M_to_i32.c', - sfdir / 'f128M_to_i64.c', - sfdir / 'f128M_to_ui32_r_minMag.c', - sfdir / 'f128M_to_ui64_r_minMag.c', - sfdir / 'f128M_to_i32_r_minMag.c', - sfdir / 'f128M_to_i64_r_minMag.c', - sfdir / 'f128M_to_f16.c', - sfdir / 'f128M_to_f32.c', - sfdir / 'f128M_to_extF80M.c', - sfdir / 'f128M_to_f64.c', - sfdir / 'f128M_roundToInt.c', - sfdir / 'f128M_add.c', - sfdir / 'f128M_sub.c', - sfdir / 'f128M_mul.c', - sfdir / 'f128M_mulAdd.c', - sfdir / 'f128M_div.c', - sfdir / 'f128M_rem.c', - sfdir / 'f128M_sqrt.c', - sfdir / 'f128M_eq.c', - sfdir / 'f128M_le.c', - sfdir / 'f128M_lt.c', - sfdir / 'f128M_eq_signaling.c', - sfdir / 'f128M_le_quiet.c', - sfdir / 'f128M_lt_quiet.c', - # spe - sfspedir / 'softfloat_raiseFlags.c', - sfspedir / 's_f16UIToCommonNaN.c', - sfspedir / 's_commonNaNToF16UI.c', - sfspedir / 's_propagateNaNF16UI.c', - sfspedir / 's_f32UIToCommonNaN.c', - sfspedir / 's_commonNaNToF32UI.c', - sfspedir / 's_propagateNaNF32UI.c', - sfspedir / 's_f64UIToCommonNaN.c', - sfspedir / 's_commonNaNToF64UI.c', - sfspedir / 's_propagateNaNF64UI.c', - sfspedir / 'extF80M_isSignalingNaN.c', - sfspedir / 's_extF80UIToCommonNaN.c', - sfspedir / 's_commonNaNToExtF80UI.c', - sfspedir / 's_propagateNaNExtF80UI.c', - sfspedir / 'f128M_isSignalingNaN.c', - sfspedir / 's_f128UIToCommonNaN.c', - sfspedir / 's_commonNaNToF128UI.c', - sfspedir / 's_propagateNaNF128UI.c', - ), - include_directories: sfinc, - c_args: sfcflags + fpcflags, -) - -fpcflags += [ +fpcflags = [ # work around TARGET_* poisoning '-DHW_POISON_H', # define a target to match testfloat's implementation-defined choices, such as @@ -551,10 +46,8 @@ fpcflags += [ fptest = executable( 'fp-test', - ['fp-test.c', tfdir / 'slowfloat.c', '../../fpu/softfloat.c'], - link_with: [libtestfloat, libsoftfloat], - dependencies: [qemuutil], - include_directories: [sfinc, include_directories(tfdir)], + ['fp-test.c', '../../fpu/softfloat.c'], + dependencies: [qemuutil, libsoftfloat, libtestfloat, libslowfloat], c_args: fpcflags, ) softfloat_conv_tests = { @@ -636,18 +129,14 @@ test('fp-test-mulAdd', fptest, executable( 'fp-bench', ['fp-bench.c', '../../fpu/softfloat.c'], - link_with: [libtestfloat, libsoftfloat], - dependencies: [qemuutil], - include_directories: [sfinc, include_directories(tfdir)], + dependencies: [qemuutil, libtestfloat, libsoftfloat], c_args: fpcflags, ) fptestlog2 = executable( 'fp-test-log2', ['fp-test-log2.c', '../../fpu/softfloat.c'], - link_with: [libsoftfloat], - dependencies: [qemuutil], - include_directories: [sfinc], + dependencies: [qemuutil, libsoftfloat], c_args: fpcflags, ) test('fp-test-log2', fptestlog2, From 1f468152fb2d461f2a5f8838a3bea9fd6aaa2207 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 19 May 2023 13:27:10 +0200 Subject: [PATCH 184/412] build: remove git submodule handling from main makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only remaining user of submodules at build time is roms/SLOF, which is handled in pc-bios/s390-ccw/Makefile. Remove the relevant code from the main makefile. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest-template.yml | 3 +-- Makefile | 10 ---------- configure | 7 ------- meson.build | 1 - 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index d01d504ec5..76ff1dfcb6 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -42,8 +42,7 @@ stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest script: - - scripts/git-submodule.sh update - roms/SLOF $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) + - scripts/git-submodule.sh update roms/SLOF - meson subprojects download $(cd build/subprojects && echo *) - cd build - find . -type f -exec touch {} + diff --git a/Makefile b/Makefile index d68196acb9..b22bf6fba1 100644 --- a/Makefile +++ b/Makefile @@ -45,16 +45,6 @@ include config-host.mak include Makefile.prereqs Makefile.prereqs: config-host.mak -git-submodule-update: -.git-submodule-status: git-submodule-update config-host.mak -Makefile: .git-submodule-status - -.PHONY: git-submodule-update -git-submodule-update: -ifneq ($(GIT_SUBMODULES_ACTION),ignore) - $(quiet-@)GIT=git "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES) -endif - # 0. ensure the build tree is okay # Check that we're not trying to do an out-of-tree build from diff --git a/configure b/configure index 4dad32938d..bc0660f5a3 100755 --- a/configure +++ b/configure @@ -253,7 +253,6 @@ else git_submodules_action="ignore" fi -git_submodules="" git="git" debug_tcg="no" docs="auto" @@ -1657,18 +1656,12 @@ fi ####################################### # generate config-host.mak -if ! (GIT=git "$source_path/scripts/git-submodule.sh" "$git_submodules_action" "$git_submodules"); then - exit 1 -fi - config_host_mak="config-host.mak" echo "# Automatically generated by configure - do not modify" > $config_host_mak echo >> $config_host_mak echo all: >> $config_host_mak -echo "GIT_SUBMODULES=$git_submodules" >> $config_host_mak -echo "GIT_SUBMODULES_ACTION=$git_submodules_action" >> $config_host_mak if test "$debug_tcg" = "yes" ; then echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak diff --git a/meson.build b/meson.build index 74fb147fde..6cd29c11b0 100644 --- a/meson.build +++ b/meson.build @@ -3976,7 +3976,6 @@ endif summary_info = {} summary_info += {'Build directory': meson.current_build_dir()} summary_info += {'Source path': meson.current_source_dir()} -summary_info += {'GIT submodules': config_host['GIT_SUBMODULES']} summary_info += {'Download dependencies': get_option('wrap_mode') != 'nodownload'} summary(summary_info, bool_yn: true, section: 'Build environment') From 6f3ae23b29ad5831902e3ecdc7e443bbbf295bde Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 May 2023 16:03:50 +0200 Subject: [PATCH 185/412] configure: remove --with-git-submodules= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse --enable/--disable-download to control git submodules as well. Adjust the error messages of git-submodule.sh to refer to the new option. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- configure | 40 +++++-------------- .../ci/org.centos/stream/8/x86_64/configure | 1 - scripts/git-submodule.sh | 8 ++-- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/configure b/configure index bc0660f5a3..8765b88e12 100755 --- a/configure +++ b/configure @@ -246,13 +246,7 @@ for opt do done -if test -e "$source_path/.git" -then - git_submodules_action="update" -else - git_submodules_action="ignore" -fi - +git_submodules_action="update" git="git" debug_tcg="no" docs="auto" @@ -738,12 +732,9 @@ for opt do ;; --disable-cfi) cfi="false" ;; - --with-git-submodules=*) - git_submodules_action="$optarg" + --disable-download) download="disabled"; git_submodules_action=validate; ;; - --disable-download) download="disabled" - ;; - --enable-download) download="enabled" + --enable-download) download="enabled"; git_submodules_action=update; ;; --enable-plugins) if test "$mingw32" = "yes"; then error_exit "TCG plugins not currently supported on Windows platforms" @@ -765,6 +756,11 @@ for opt do esac done +if ! test -e "$source_path/.git" +then + git_submodules_action="ignore" +fi + # test for any invalid configuration combinations if test "$plugins" = "yes" -a "$tcg" = "disabled"; then error_exit "Can't enable plugins on non-TCG builds" @@ -796,21 +792,6 @@ then exit 1 fi -case $git_submodules_action in - update|validate) - if test ! -e "$source_path/.git" || ! has git; then - echo "ERROR: cannot $git_submodules_action git submodules without .git" - exit 1 - fi - ;; - ignore) - ;; - *) - echo "ERROR: invalid --with-git-submodules= value '$git_submodules_action'" - exit 1 - ;; -esac - default_target_list="" mak_wilds="" @@ -877,9 +858,6 @@ Advanced options (experts only): --python=PYTHON use specified python [$python] --ninja=NINJA use specified ninja [$ninja] --smbd=SMBD use specified smbd [$smbd] - --with-git-submodules=update update git submodules (default if .git dir exists) - --with-git-submodules=validate fail if git submodules are not up to date - --with-git-submodules=ignore do not update or check git submodules (default if no .git dir) --static enable static build [$static] --bindir=PATH install binaries in PATH --with-suffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir/docdir [$qemu_suffix] @@ -1024,7 +1002,7 @@ fi # Consult white-list to determine whether to enable werror # by default. Only enable by default for git builds if test -z "$werror" ; then - if test "$git_submodules_action" != "ignore" && \ + if test -e "$source_path/.git" && \ { test "$linux" = "yes" || test "$mingw32" = "yes"; }; then werror="yes" else diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure index de76510978..d02b09a4b9 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ b/scripts/ci/org.centos/stream/8/x86_64/configure @@ -29,7 +29,6 @@ --extra-cflags="-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection" \ --with-suffix="qemu-kvm" \ --firmwarepath=/usr/share/qemu-firmware \ ---with-git-submodules=update \ --target-list="x86_64-softmmu" \ --block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \ --audio-drv-list="" \ diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index 38b55c90e1..11fad2137c 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -9,7 +9,7 @@ command=$1 shift maybe_modules="$@" -# if --with-git-submodules=ignore, do nothing +# if not running in a git checkout, do nothing test "$command" = "ignore" && exit 0 test -z "$GIT" && GIT=$(command -v git) @@ -24,7 +24,7 @@ update_error() { echo "enable use of a transparent proxy), please disable automatic" echo "GIT submodule checkout with:" echo - echo " $ ./configure --with-git-submodules=validate" + echo " $ ./configure --disable-download" echo echo "and then manually update submodules prior to running make, with:" echo @@ -39,9 +39,7 @@ validate_error() { echo "configured for validate only. Please run" echo " scripts/git-submodule.sh update $maybe_modules" echo "from the source directory or call configure with" - echo " --with-git-submodules=update" - echo "To disable GIT submodules validation, use" - echo " --with-git-submodules=ignore" + echo " --enable-download" fi exit 1 } From c0dde5fc5ccce56b69095bc29af72987efd65d1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 10:16:29 -0700 Subject: [PATCH 186/412] accel/tcg: Fix undefined shift in store_whole_le16 The computation is documented as unused in this case, but triggers an ubsan error: ../accel/tcg/ldst_atomicity.c.inc:837:33: runtime error: shift exponent -32 is negative SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../accel/tcg/ldst_atomicity.c.inc:837:33 in Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-Id: <20230606171629.98157-1-richard.henderson@linaro.org> --- accel/tcg/ldst_atomicity.c.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/accel/tcg/ldst_atomicity.c.inc b/accel/tcg/ldst_atomicity.c.inc index 2514899408..de70531a7a 100644 --- a/accel/tcg/ldst_atomicity.c.inc +++ b/accel/tcg/ldst_atomicity.c.inc @@ -833,7 +833,9 @@ static uint64_t store_whole_le16(void *pv, int size, Int128 val_le) } store_atom_insert_al16(pv - o, v, m); - /* Unused if sz <= 64. */ + if (sz <= 64) { + return 0; + } return int128_gethi(val_le) >> (sz - 64); } From e58e55dd8d5777f8a58ce30cfe04a8023282eb80 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jun 2023 10:00:21 +0200 Subject: [PATCH 187/412] meson: fix "static build" entry in summary Fixes: a0cbd2e8496 ("meson: use prefer_static option", 2023-05-18) Signed-off-by: Paolo Bonzini --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 553c8e0b9c..c03326c922 100644 --- a/meson.build +++ b/meson.build @@ -4088,7 +4088,7 @@ summary_info += {'QEMU_LDFLAGS': ' '.join(qemu_ldflags)} summary_info += {'profiler': get_option('profiler')} summary_info += {'link-time optimization (LTO)': get_option('b_lto')} summary_info += {'PIE': get_option('b_pie')} -summary_info += {'static build': config_host.has_key('CONFIG_STATIC')} +summary_info += {'static build': get_option('prefer_static')} summary_info += {'malloc trim support': has_malloc_trim} summary_info += {'membarrier': have_membarrier} summary_info += {'debug graph lock': get_option('debug_graph_lock')} From c06b1571cc5c27b2042315b2a1c99f477a497716 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Wed, 7 Jun 2023 10:01:03 +0200 Subject: [PATCH 188/412] configure: check for $download value properly If configure was invoked with --disable-download and git submodules were not checked out a warning is produced and the configure script fails. But the $download variable (which reflects the enable/disable download argument) is checked for in a weird fashion: test -f "$download" = disabled Drop the '-f' to check for the actual value of the variable. Fixes: 2019cabfee0 ("meson: subprojects: replace submodules with wrap files", 2023-06-06) Signed-off-by: Michal Privoznik Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 8765b88e12..8a638dd82a 100755 --- a/configure +++ b/configure @@ -767,7 +767,7 @@ if test "$plugins" = "yes" -a "$tcg" = "disabled"; then fi if ! test -f "$source_path/subprojects/keycodemapdb/README" \ - && test -f "$download" = disabled + && test "$download" = disabled then echo echo "ERROR: missing subprojects" From 45904b56d5321be5f6e2c9e12bd143fb3b871ca8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jun 2023 11:00:09 +0200 Subject: [PATCH 189/412] tests: fp: remove unused submodules tests/fp/berkeley-softfloat-3 and tests/fp/berkeley-testfloat-3 have been replaced by subprojects, so remove the now-unnecessary submodules. Reported-by: Michal Privoznik Signed-off-by: Paolo Bonzini --- tests/fp/berkeley-softfloat-3 | 1 - tests/fp/berkeley-testfloat-3 | 1 - 2 files changed, 2 deletions(-) delete mode 160000 tests/fp/berkeley-softfloat-3 delete mode 160000 tests/fp/berkeley-testfloat-3 diff --git a/tests/fp/berkeley-softfloat-3 b/tests/fp/berkeley-softfloat-3 deleted file mode 160000 index b64af41c32..0000000000 --- a/tests/fp/berkeley-softfloat-3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b64af41c3276f97f0e181920400ee056b9c88037 diff --git a/tests/fp/berkeley-testfloat-3 b/tests/fp/berkeley-testfloat-3 deleted file mode 160000 index 40619cbb3b..0000000000 --- a/tests/fp/berkeley-testfloat-3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 40619cbb3bf32872df8c53cc457039229428a263 From eeedfe6c6316e8c0d58becc427e12aceb4cb3ad3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2023 19:50:58 +0100 Subject: [PATCH 190/412] hw/xen: Simplify emulated Xen platform init I initially put the basic platform init (overlay pages, grant tables, event channels) into mc->kvm_type because that was the earliest place that could sensibly test for xen_mode==XEN_EMULATE. The intent was to do this early enough that we could then initialise the XenBus and other parts which would have depended on them, from a generic location for both Xen and KVM/Xen in the PC-specific code, as seen in https://lore.kernel.org/qemu-devel/20230116221919.1124201-16-dwmw2@infradead.org/ However, then the Xen on Arm patches came along, and *they* wanted to do the XenBus init from a 'generic' Xen-specific location instead: https://lore.kernel.org/qemu-devel/20230210222729.957168-4-sstabellini@kernel.org/ Since there's no generic location that covers all three, I conceded to do it for XEN_EMULATE mode in pc_basic_devices_init(). And now there's absolutely no point in having some of the platform init done from pc_machine_kvm_type(); we can move it all up to live in a single place in pc_basic_devices_init(). This has the added benefit that we can drop the separate xen_evtchn_connect_gsis() function completely, and pass just the system GSIs in directly to xen_evtchn_create(). While I'm at it, it does no harm to explicitly pass in the *number* of said GSIs, because it does make me twitch a bit to pass an array of impicit size. During the lifetime of the KVM/Xen patchset, that had already changed (albeit just cosmetically) from GSI_NUM_PINS to IOAPIC_NUM_PINS. And document a bit better that this is for the *output* GSI for raising CPU0's events when the per-CPU vector isn't available. The fact that we create a whole set of them and then only waggle the one we're told to, instead of having a single output and only *connecting* it to the GSI that it should be connected to, is still non-intuitive for me. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant Message-Id: <20230412185102.441523-2-dwmw2@infradead.org> Signed-off-by: Anthony PERARD --- hw/i386/kvm/xen_evtchn.c | 40 ++++++++++++++++++++-------------------- hw/i386/kvm/xen_evtchn.h | 3 +-- hw/i386/pc.c | 13 ++++--------- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 3048329474..3d810dbd59 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -147,7 +147,10 @@ struct XenEvtchnState { QemuMutex port_lock; uint32_t nr_ports; XenEvtchnPort port_table[EVTCHN_2L_NR_CHANNELS]; - qemu_irq gsis[IOAPIC_NUM_PINS]; + + /* Connected to the system GSIs for raising callback as GSI / INTx */ + unsigned int nr_callback_gsis; + qemu_irq *callback_gsis; struct xenevtchn_handle *be_handles[EVTCHN_2L_NR_CHANNELS]; @@ -299,7 +302,7 @@ static void gsi_assert_bh(void *opaque) } } -void xen_evtchn_create(void) +void xen_evtchn_create(unsigned int nr_gsis, qemu_irq *system_gsis) { XenEvtchnState *s = XEN_EVTCHN(sysbus_create_simple(TYPE_XEN_EVTCHN, -1, NULL)); @@ -310,8 +313,19 @@ void xen_evtchn_create(void) qemu_mutex_init(&s->port_lock); s->gsi_bh = aio_bh_new(qemu_get_aio_context(), gsi_assert_bh, s); - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gsis[i]); + /* + * These are the *output* GSI from event channel support, for + * signalling CPU0's events via GSI or PCI INTx instead of the + * per-CPU vector. We create a *set* of irqs and connect one to + * each of the system GSIs which were passed in from the platform + * code, and then just trigger the right one as appropriate from + * xen_evtchn_set_callback_level(). + */ + s->nr_callback_gsis = nr_gsis; + s->callback_gsis = g_new0(qemu_irq, nr_gsis); + for (i = 0; i < nr_gsis; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->callback_gsis[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(s), i, system_gsis[i]); } /* @@ -336,20 +350,6 @@ void xen_evtchn_create(void) xen_evtchn_ops = &emu_evtchn_backend_ops; } -void xen_evtchn_connect_gsis(qemu_irq *system_gsis) -{ - XenEvtchnState *s = xen_evtchn_singleton; - int i; - - if (!s) { - return; - } - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(s), i, system_gsis[i]); - } -} - static void xen_evtchn_register_types(void) { type_register_static(&xen_evtchn_info); @@ -430,8 +430,8 @@ void xen_evtchn_set_callback_level(int level) return; } - if (s->callback_gsi && s->callback_gsi < IOAPIC_NUM_PINS) { - qemu_set_irq(s->gsis[s->callback_gsi], level); + if (s->callback_gsi && s->callback_gsi < s->nr_callback_gsis) { + qemu_set_irq(s->callback_gsis[s->callback_gsi], level); if (level) { /* Ensure the vCPU polls for deassertion */ kvm_xen_set_callback_asserted(); diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index bfb67ac2bc..b740acfc0d 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -16,10 +16,9 @@ typedef uint32_t evtchn_port_t; -void xen_evtchn_create(void); +void xen_evtchn_create(unsigned int nr_gsis, qemu_irq *system_gsis); int xen_evtchn_soft_reset(void); int xen_evtchn_set_callback_param(uint64_t param); -void xen_evtchn_connect_gsis(qemu_irq *system_gsis); void xen_evtchn_set_callback_level(int level); int xen_evtchn_set_port(uint16_t port); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index bb62c994fa..fc52772fdd 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1332,7 +1332,10 @@ void pc_basic_device_init(struct PCMachineState *pcms, #ifdef CONFIG_XEN_EMU if (xen_mode == XEN_EMULATE) { - xen_evtchn_connect_gsis(gsi); + xen_overlay_create(); + xen_evtchn_create(IOAPIC_NUM_PINS, gsi); + xen_gnttab_create(); + xen_xenstore_create(); if (pcms->bus) { pci_create_simple(pcms->bus, -1, "xen-platform"); } @@ -1882,14 +1885,6 @@ static void pc_machine_initfn(Object *obj) int pc_machine_kvm_type(MachineState *machine, const char *kvm_type) { -#ifdef CONFIG_XEN_EMU - if (xen_mode == XEN_EMULATE) { - xen_overlay_create(); - xen_evtchn_create(); - xen_gnttab_create(); - xen_xenstore_create(); - } -#endif return 0; } From 8442232eba1b041b379ca5845df8252c1e905e43 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2023 19:50:59 +0100 Subject: [PATCH 191/412] hw/xen: Fix memory leak in libxenstore_open() for Xen There was a superfluous allocation of the XS handle, leading to it being leaked on both the error path and the success path (where it gets allocated again). Spotted by Coverity (CID 1508098). Fixes: ba2a92db1ff6 ("hw/xen: Add xenstore operations to allow redirection to internal emulation") Suggested-by: Peter Maydell Signed-off-by: David Woodhouse Reviewed-by: Peter Maydell Reviewed-by: Paul Durrant Message-Id: <20230412185102.441523-3-dwmw2@infradead.org> Signed-off-by: Anthony PERARD --- hw/xen/xen-operations.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/xen/xen-operations.c b/hw/xen/xen-operations.c index 4b78fbf4bd..3d213d28df 100644 --- a/hw/xen/xen-operations.c +++ b/hw/xen/xen-operations.c @@ -287,7 +287,7 @@ static void watch_event(void *opaque) static struct qemu_xs_handle *libxenstore_open(void) { struct xs_handle *xsh = xs_open(0); - struct qemu_xs_handle *h = g_new0(struct qemu_xs_handle, 1); + struct qemu_xs_handle *h; if (!xsh) { return NULL; From 2f20b1732d44c7eb1abcb611fdb07a96dbf40a17 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2023 19:51:00 +0100 Subject: [PATCH 192/412] xen: Drop support for Xen versions below 4.7.1 In restructuring to allow for internal emulation of Xen functionality, I broke compatibility for Xen 4.6 and earlier. Fix this by explicitly removing support for anything older than 4.7.1, which is also ancient but it does still build, and the compatibility support for it is fairly unintrusive. Fixes: 15e283c5b684 ("hw/xen: Add foreignmem operations to allow redirection to internal emulation") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant Message-Id: <20230412185102.441523-4-dwmw2@infradead.org> Signed-off-by: Anthony PERARD --- hw/xen/xen-operations.c | 57 +------------------ include/hw/xen/xen_native.h | 107 +----------------------------------- meson.build | 5 +- scripts/xen-detect.c | 60 -------------------- 4 files changed, 3 insertions(+), 226 deletions(-) diff --git a/hw/xen/xen-operations.c b/hw/xen/xen-operations.c index 3d213d28df..e00983ec44 100644 --- a/hw/xen/xen-operations.c +++ b/hw/xen/xen-operations.c @@ -28,46 +28,13 @@ #include /* - * We don't support Xen prior to 4.2.0. + * We don't support Xen prior to 4.7.1. */ -/* Xen 4.2 through 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701 - -typedef xc_evtchn xenevtchn_handle; -typedef evtchn_port_or_error_t xenevtchn_port_or_error_t; - -#define xenevtchn_open(l, f) xc_evtchn_open(l, f); -#define xenevtchn_close(h) xc_evtchn_close(h) -#define xenevtchn_fd(h) xc_evtchn_fd(h) -#define xenevtchn_pending(h) xc_evtchn_pending(h) -#define xenevtchn_notify(h, p) xc_evtchn_notify(h, p) -#define xenevtchn_bind_interdomain(h, d, p) xc_evtchn_bind_interdomain(h, d, p) -#define xenevtchn_unmask(h, p) xc_evtchn_unmask(h, p) -#define xenevtchn_unbind(h, p) xc_evtchn_unbind(h, p) - -typedef xc_gnttab xengnttab_handle; - -#define xengnttab_open(l, f) xc_gnttab_open(l, f) -#define xengnttab_close(h) xc_gnttab_close(h) -#define xengnttab_set_max_grants(h, n) xc_gnttab_set_max_grants(h, n) -#define xengnttab_map_grant_ref(h, d, r, p) xc_gnttab_map_grant_ref(h, d, r, p) -#define xengnttab_unmap(h, a, n) xc_gnttab_munmap(h, a, n) -#define xengnttab_map_grant_refs(h, c, d, r, p) \ - xc_gnttab_map_grant_refs(h, c, d, r, p) -#define xengnttab_map_domain_grant_refs(h, c, d, r, p) \ - xc_gnttab_map_domain_grant_refs(h, c, d, r, p) - -typedef xc_interface xenforeignmemory_handle; - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */ - #include #include #include -#endif - /* Xen before 4.8 */ static int libxengnttab_fallback_grant_copy(xengnttab_handle *xgt, @@ -223,26 +190,6 @@ static struct gnttab_backend_ops libxengnttab_backend_ops = { .unmap = libxengnttab_backend_unmap, }; -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701 - -static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot, - size_t pages, xfn_pfn_t *pfns, - int *errs) -{ - if (errs) { - return xc_map_foreign_bulk(xen_xc, dom, prot, pfns, errs, pages); - } else { - return xc_map_foreign_pages(xen_xc, dom, prot, pfns, pages); - } -} - -static int libxenforeignmem_backend_unmap(void *addr, size_t pages) -{ - return munmap(addr, pages * XC_PAGE_SIZE); -} - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */ - static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot, size_t pages, xen_pfn_t *pfns, int *errs) @@ -256,8 +203,6 @@ static int libxenforeignmem_backend_unmap(void *addr, size_t pages) return xenforeignmemory_unmap(xen_fmem, addr, pages); } -#endif - struct foreignmem_backend_ops libxenforeignmem_backend_ops = { .map = libxenforeignmem_backend_map, .unmap = libxenforeignmem_backend_unmap, diff --git a/include/hw/xen/xen_native.h b/include/hw/xen/xen_native.h index 6bcc83baf9..f11eb423e3 100644 --- a/include/hw/xen/xen_native.h +++ b/include/hw/xen/xen_native.h @@ -24,23 +24,11 @@ extern xc_interface *xen_xc; /* - * We don't support Xen prior to 4.2.0. + * We don't support Xen prior to 4.7.1. */ -/* Xen 4.2 through 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701 - -typedef xc_interface xenforeignmemory_handle; - -#define xenforeignmemory_open(l, f) xen_xc -#define xenforeignmemory_close(h) - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */ - #include -#endif - extern xenforeignmemory_handle *xen_fmem; #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 @@ -148,8 +136,6 @@ static inline xendevicemodel_handle *xendevicemodel_open( return xen_xc; } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40500 - static inline int xendevicemodel_create_ioreq_server( xendevicemodel_handle *dmod, domid_t domid, int handle_bufioreq, ioservid_t *id) @@ -211,8 +197,6 @@ static inline int xendevicemodel_set_ioreq_server_state( return xc_hvm_set_ioreq_server_state(dmod, domid, id, enabled); } -#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40500 */ - static inline int xendevicemodel_set_pci_intx_level( xendevicemodel_handle *dmod, domid_t domid, uint16_t segment, uint8_t bus, uint8_t device, uint8_t intx, unsigned int level) @@ -340,15 +324,6 @@ static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, } #endif -/* Xen before 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40600 - -#ifndef HVM_IOREQSRV_BUFIOREQ_ATOMIC -#define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2 -#endif - -#endif - static inline int xen_get_default_ioreq_server_info(domid_t dom, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, @@ -386,84 +361,6 @@ static inline int xen_get_default_ioreq_server_info(domid_t dom, return 0; } -/* Xen before 4.5 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40500 - -#ifndef HVM_PARAM_BUFIOREQ_EVTCHN -#define HVM_PARAM_BUFIOREQ_EVTCHN 26 -#endif - -#define IOREQ_TYPE_PCI_CONFIG 2 - -typedef uint16_t ioservid_t; - -static inline void xen_map_memory_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_unmap_memory_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_map_io_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_unmap_io_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_map_pcidev(domid_t dom, - ioservid_t ioservid, - PCIDevice *pci_dev) -{ -} - -static inline void xen_unmap_pcidev(domid_t dom, - ioservid_t ioservid, - PCIDevice *pci_dev) -{ -} - -static inline void xen_create_ioreq_server(domid_t dom, - ioservid_t *ioservid) -{ -} - -static inline void xen_destroy_ioreq_server(domid_t dom, - ioservid_t ioservid) -{ -} - -static inline int xen_get_ioreq_server_info(domid_t dom, - ioservid_t ioservid, - xen_pfn_t *ioreq_pfn, - xen_pfn_t *bufioreq_pfn, - evtchn_port_t *bufioreq_evtchn) -{ - return xen_get_default_ioreq_server_info(dom, ioreq_pfn, - bufioreq_pfn, - bufioreq_evtchn); -} - -static inline int xen_set_ioreq_server_state(domid_t dom, - ioservid_t ioservid, - bool enable) -{ - return 0; -} - -/* Xen 4.5 */ -#else - static bool use_default_ioreq_server; static inline void xen_map_memory_section(domid_t dom, @@ -624,6 +521,4 @@ static inline int xen_set_ioreq_server_state(domid_t dom, enable); } -#endif - #endif /* QEMU_HW_XEN_NATIVE_H */ diff --git a/meson.build b/meson.build index 553c8e0b9c..0984237c48 100644 --- a/meson.build +++ b/meson.build @@ -1683,16 +1683,13 @@ if get_option('xen').enabled() or (get_option('xen').auto() and have_system) endif endif if not xen.found() - xen_tests = [ '4.11.0', '4.10.0', '4.9.0', '4.8.0', '4.7.1', '4.6.0', '4.5.0', '4.2.0' ] + xen_tests = [ '4.11.0', '4.10.0', '4.9.0', '4.8.0', '4.7.1' ] xen_libs = { '4.11.0': [ 'xenstore', 'xenctrl', 'xendevicemodel', 'xenforeignmemory', 'xengnttab', 'xenevtchn', 'xentoolcore' ], '4.10.0': [ 'xenstore', 'xenctrl', 'xendevicemodel', 'xenforeignmemory', 'xengnttab', 'xenevtchn', 'xentoolcore' ], '4.9.0': [ 'xenstore', 'xenctrl', 'xendevicemodel', 'xenforeignmemory', 'xengnttab', 'xenevtchn' ], '4.8.0': [ 'xenstore', 'xenctrl', 'xenforeignmemory', 'xengnttab', 'xenevtchn' ], '4.7.1': [ 'xenstore', 'xenctrl', 'xenforeignmemory', 'xengnttab', 'xenevtchn' ], - '4.6.0': [ 'xenstore', 'xenctrl' ], - '4.5.0': [ 'xenstore', 'xenctrl' ], - '4.2.0': [ 'xenstore', 'xenctrl' ], } xen_deps = {} foreach ver: xen_tests diff --git a/scripts/xen-detect.c b/scripts/xen-detect.c index 85e8206490..db049e605c 100644 --- a/scripts/xen-detect.c +++ b/scripts/xen-detect.c @@ -138,66 +138,6 @@ return 0; } -#elif CONFIG_XEN_CTRL_INTERFACE_VERSION == 40600 - #include - #include - #include - #include - #if !defined(HVM_MAX_VCPUS) - # error HVM_MAX_VCPUS not defined - #endif - int main(void) { - xc_interface *xc; - xs_daemon_open(); - xc = xc_interface_open(0, 0, 0); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); - xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); - xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - xc_hvm_create_ioreq_server(xc, 0, HVM_IOREQSRV_BUFIOREQ_ATOMIC, NULL); - xc_reserved_device_memory_map(xc, 0, 0, 0, 0, NULL, 0); - return 0; - } - -#elif CONFIG_XEN_CTRL_INTERFACE_VERSION == 40500 - #include - #include - #include - #include - #if !defined(HVM_MAX_VCPUS) - # error HVM_MAX_VCPUS not defined - #endif - int main(void) { - xc_interface *xc; - xs_daemon_open(); - xc = xc_interface_open(0, 0, 0); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); - xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); - xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - xc_hvm_create_ioreq_server(xc, 0, 0, NULL); - return 0; - } - -#elif CONFIG_XEN_CTRL_INTERFACE_VERSION == 40200 - #include - #include - #include - #include - #if !defined(HVM_MAX_VCPUS) - # error HVM_MAX_VCPUS not defined - #endif - int main(void) { - xc_interface *xc; - xs_daemon_open(); - xc = xc_interface_open(0, 0, 0); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); - xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); - xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - return 0; - } - #else #error invalid CONFIG_XEN_CTRL_INTERFACE_VERSION #endif From c9bdfe8d587c1a6a8fc2e0ff97343745a9f5f247 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2023 19:51:02 +0100 Subject: [PATCH 193/412] hw/xen: Fix broken check for invalid state in xs_be_open() Coverity points out that if (!s && !s->impl) isn't really what we intended to do here. CID 1508131. Fixes: 032475127225 ("hw/xen: Add emulated implementation of XenStore operations") Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant Reviewed-by: Peter Maydell Message-Id: <20230412185102.441523-6-dwmw2@infradead.org> Signed-off-by: Anthony PERARD --- hw/i386/kvm/xen_xenstore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 0b189c6ab8..133d89e953 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -1688,7 +1688,7 @@ static struct qemu_xs_handle *xs_be_open(void) XenXenstoreState *s = xen_xenstore_singleton; struct qemu_xs_handle *h; - if (!s && !s->impl) { + if (!s || !s->impl) { errno = -ENOSYS; return NULL; } From 27047bd26633d559b9630c57e922e4e78cb2ff1d Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:18 +0200 Subject: [PATCH 194/412] include/hw/xen/xen: Rename xen_piix3_set_irq() to xen_intx_set_irq() xen_piix3_set_irq() isn't PIIX specific: PIIX is a single PCI device while xen_piix3_set_irq() maps multiple PCI devices to their respective IRQs, which is board-specific. Rename xen_piix3_set_irq() to communicate this. Also rename XEN_PIIX_NUM_PIRQS to XEN_IOAPIC_NUM_PIRQS since the Xen's IOAPIC rather than PIIX has this many interrupt routes. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Reviewed-by: Anthony PERARD Tested-by: Chuck Zmudzinski Message-Id: <20230312120221.99183-2-shentey@gmail.com> Message-Id: <20230403074124.3925-2-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/i386/xen/xen-hvm.c | 2 +- hw/isa/piix3.c | 4 ++-- include/hw/xen/xen.h | 2 +- stubs/xen-hw-stub.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 56641a550e..ab8f1b61ee 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -143,7 +143,7 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) return irq_num + (PCI_SLOT(pci_dev->devfn) << 2); } -void xen_piix3_set_irq(void *opaque, int irq_num, int level) +void xen_intx_set_irq(void *opaque, int irq_num, int level) { xen_set_pci_intx_level(xen_domid, 0, 0, irq_num >> 2, irq_num & 3, level); diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index f9103ea45a..6651521a46 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -35,7 +35,7 @@ #include "migration/vmstate.h" #include "hw/acpi/acpi_aml_interface.h" -#define XEN_PIIX_NUM_PIRQS 128ULL +#define XEN_IOAPIC_NUM_PIRQS 128ULL static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) { @@ -420,7 +420,7 @@ static void piix3_xen_realize(PCIDevice *dev, Error **errp) * connected to the IOAPIC directly. * These additional routes can be discovered through ACPI. */ - pci_bus_irqs(pci_bus, xen_piix3_set_irq, piix3, XEN_PIIX_NUM_PIRQS); + pci_bus_irqs(pci_bus, xen_intx_set_irq, piix3, XEN_IOAPIC_NUM_PIRQS); } static void piix3_xen_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 2bd8ec742d..37ecc91fc3 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -39,7 +39,7 @@ extern bool xen_domid_restrict; int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); int xen_set_pci_link_route(uint8_t link, uint8_t irq); -void xen_piix3_set_irq(void *opaque, int irq_num, int level); +void xen_intx_set_irq(void *opaque, int irq_num, int level); void xen_hvm_inject_msi(uint64_t addr, uint32_t data); int xen_is_pirq_msi(uint32_t msi_data); diff --git a/stubs/xen-hw-stub.c b/stubs/xen-hw-stub.c index 34a22f2ad7..7d7ffe83a9 100644 --- a/stubs/xen-hw-stub.c +++ b/stubs/xen-hw-stub.c @@ -15,7 +15,7 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) return -1; } -void xen_piix3_set_irq(void *opaque, int irq_num, int level) +void xen_intx_set_irq(void *opaque, int irq_num, int level) { } From c0b59416c05fbf7a5fefee4b4757f7281e34ed02 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:19 +0200 Subject: [PATCH 195/412] hw/pci/pci.c: Don't leak PCIBus::irq_count[] in pci_bus_irqs() When calling pci_bus_irqs() multiple times on the same object without calling pci_bus_irqs_cleanup() in between PCIBus::irq_count[] is currently leaked. Let's fix this because Xen will do just that in a few commits, and because calling pci_bus_irqs_cleanup() in between seems fragile and cumbersome. Note that pci_bus_irqs_cleanup() now has to NULL irq_count such that pci_bus_irqs() doesn't do a double free. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Tested-by: Chuck Zmudzinski Message-Id: <20230403074124.3925-3-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/pci/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1cc7c89036..9b7b4d7c18 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -560,6 +560,7 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, bus->set_irq = set_irq; bus->irq_opaque = irq_opaque; bus->nirq = nirq; + g_free(bus->irq_count); bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); } @@ -575,6 +576,7 @@ void pci_bus_irqs_cleanup(PCIBus *bus) bus->irq_opaque = NULL; bus->nirq = 0; g_free(bus->irq_count); + bus->irq_count = NULL; } PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, From a58a31a6a18ab650c6b676eccf98b6f68ac672d0 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:20 +0200 Subject: [PATCH 196/412] hw/isa/piix3: Reuse piix3_realize() in piix3_xen_realize() This is a preparational patch for the next one to make the following more obvious: First, pci_bus_irqs() is now called twice in case of Xen where the second call overrides the pci_set_irq_fn with the Xen variant. Second, pci_bus_set_route_irq_fn() is now also called in Xen mode. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Reviewed-by: Anthony PERARD Tested-by: Chuck Zmudzinski Message-Id: <20230312120221.99183-3-shentey@gmail.com> Message-Id: <20230403074124.3925-4-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/isa/piix3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 6651521a46..800b80f2bb 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -409,7 +409,7 @@ static void piix3_xen_realize(PCIDevice *dev, Error **errp) PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); PCIBus *pci_bus = pci_get_bus(dev); - pci_piix3_realize(dev, errp); + piix3_realize(dev, errp); if (*errp) { return; } From 60a9eb57f337ee36cb9140154a83edb2c41f00c6 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:21 +0200 Subject: [PATCH 197/412] hw/isa/piix3: Wire up Xen PCI IRQ handling outside of PIIX3 xen_intx_set_irq() doesn't depend on PIIX3State. In order to resolve TYPE_PIIX3_XEN_DEVICE and in order to make Xen agnostic about the precise south bridge being used, set up Xen's PCI IRQ handling of PIIX3 in the board. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Reviewed-by: Anthony PERARD Tested-by: Chuck Zmudzinski Message-Id: <20230312120221.99183-4-shentey@gmail.com> Message-Id: <20230403074124.3925-5-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/i386/pc_piix.c | 13 +++++++++++++ hw/isa/piix3.c | 24 +----------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d5b0dcd1fe..11af191513 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -71,6 +71,7 @@ #include "kvm/kvm-cpu.h" #define MAX_IDE_BUS 2 +#define XEN_IOAPIC_NUM_PIRQS 128ULL #ifdef CONFIG_IDE_ISA static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; @@ -238,6 +239,18 @@ static void pc_init1(MachineState *machine, pcms->bus = pci_bus; pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, type); + + if (xen_enabled()) { + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pci_bus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } + piix3 = PIIX3_PCI_DEVICE(pci_dev); piix3->pic = x86ms->gsi; piix3_devfn = piix3->dev.devfn; diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 800b80f2bb..0b7e9ade0d 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -35,8 +35,6 @@ #include "migration/vmstate.h" #include "hw/acpi/acpi_aml_interface.h" -#define XEN_IOAPIC_NUM_PIRQS 128ULL - static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) { qemu_set_irq(piix3->pic[pic_irq], @@ -403,32 +401,12 @@ static const TypeInfo piix3_info = { .class_init = piix3_class_init, }; -static void piix3_xen_realize(PCIDevice *dev, Error **errp) -{ - ERRP_GUARD(); - PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); - PCIBus *pci_bus = pci_get_bus(dev); - - piix3_realize(dev, errp); - if (*errp) { - return; - } - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - pci_bus_irqs(pci_bus, xen_intx_set_irq, piix3, XEN_IOAPIC_NUM_PIRQS); -} - static void piix3_xen_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->config_write = piix3_write_config_xen; - k->realize = piix3_xen_realize; + k->realize = piix3_realize; } static const TypeInfo piix3_xen_info = { From 89965db43cce708216de860b3655b1a4816e9fa9 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:22 +0200 Subject: [PATCH 198/412] hw/isa/piix3: Avoid Xen-specific variant of piix3_write_config() Subscribe to pci_bus_fire_intx_routing_notifier() instead which allows for having a common piix3_write_config() for the PIIX3 device models. While at it, move the subscription into machine code to facilitate resolving TYPE_PIIX3_XEN_DEVICE. In a possible future followup, pci_bus_fire_intx_routing_notifier() could be adjusted in such a way that subscribing to it doesn't require knowledge of the device firing it. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Reviewed-by: Anthony PERARD Tested-by: Chuck Zmudzinski Message-Id: <20230312120221.99183-5-shentey@gmail.com> Message-Id: <20230403074124.3925-6-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/i386/pc_piix.c | 18 ++++++++++++++++++ hw/isa/piix3.c | 22 +--------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 11af191513..2ed2b3f3c6 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -90,6 +90,21 @@ static int pc_pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) return (pci_intx + slot_addend) & 3; } +static void piix_intx_routing_notifier_xen(PCIDevice *dev) +{ + int i; + + /* Scan for updates to PCI link routes (0x60-0x63). */ + for (i = 0; i < PIIX_NUM_PIRQS; i++) { + uint8_t v = dev->config_read(dev, PIIX_PIRQCA + i, 1); + if (v & 0x80) { + v = 0; + } + v &= 0xf; + xen_set_pci_link_route(i, v); + } +} + /* PC hardware initialisation */ static void pc_init1(MachineState *machine, const char *host_type, const char *pci_type) @@ -241,6 +256,9 @@ static void pc_init1(MachineState *machine, pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, type); if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); + /* * Xen supports additional interrupt routes from the PCI devices to * the IOAPIC: the four pins of each PCI device on the bus are also diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 0b7e9ade0d..5e8ac98ae0 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -122,26 +122,6 @@ static void piix3_write_config(PCIDevice *dev, } } -static void piix3_write_config_xen(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - int i; - - /* Scan for updates to PCI link routes (0x60-0x63). */ - for (i = 0; i < len; i++) { - uint8_t v = (val >> (8 * i)) & 0xff; - if (v & 0x80) { - v = 0; - } - v &= 0xf; - if (((address + i) >= PIIX_PIRQCA) && ((address + i) <= PIIX_PIRQCD)) { - xen_set_pci_link_route(address + i - PIIX_PIRQCA, v); - } - } - - piix3_write_config(dev, address, val, len); -} - static void piix3_reset(DeviceState *dev) { PIIX3State *d = PIIX3_PCI_DEVICE(dev); @@ -405,7 +385,7 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->config_write = piix3_write_config_xen; + k->config_write = piix3_write_config; k->realize = piix3_realize; } From 0f3e02a2f549e8b246252610cd0c4fc4e4f501ac Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:23 +0200 Subject: [PATCH 199/412] hw/isa/piix3: Resolve redundant k->config_write assignments The previous patch unified handling of piix3_write_config() accross the PIIX3 device models which allows for assigning k->config_write once in the base class. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Reviewed-by: Anthony PERARD Tested-by: Chuck Zmudzinski Message-Id: <20230312120221.99183-6-shentey@gmail.com> Message-Id: <20230403074124.3925-7-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/isa/piix3.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 5e8ac98ae0..0549e043ca 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -322,6 +322,7 @@ static void pci_piix3_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); + k->config_write = piix3_write_config; dc->reset = piix3_reset; dc->desc = "ISA bridge"; dc->vmsd = &vmstate_piix3; @@ -371,7 +372,6 @@ static void piix3_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->config_write = piix3_write_config; k->realize = piix3_realize; } @@ -385,7 +385,6 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->config_write = piix3_write_config; k->realize = piix3_realize; } From f8790f81eb8d4a1093e27485e4424ed423e2cc1b Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 3 Apr 2023 09:41:24 +0200 Subject: [PATCH 200/412] hw/isa/piix3: Resolve redundant TYPE_PIIX3_XEN_DEVICE During the last patches, TYPE_PIIX3_XEN_DEVICE turned into a clone of TYPE_PIIX3_DEVICE. Remove this redundancy. Signed-off-by: Bernhard Beschow Reviewed-by: Michael S. Tsirkin Reviewed-by: Anthony PERARD Tested-by: Chuck Zmudzinski Message-Id: <20230312120221.99183-7-shentey@gmail.com> Message-Id: <20230403074124.3925-8-shentey@gmail.com> Signed-off-by: Anthony PERARD --- hw/i386/pc_piix.c | 5 ++--- hw/isa/piix3.c | 15 --------------- include/hw/southbridge/piix.h | 1 - 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2ed2b3f3c6..42af03dbb4 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -239,8 +239,6 @@ static void pc_init1(MachineState *machine, if (pcmc->pci_enabled) { PIIX3State *piix3; PCIDevice *pci_dev; - const char *type = xen_enabled() ? TYPE_PIIX3_XEN_DEVICE - : TYPE_PIIX3_DEVICE; pci_bus = i440fx_init(pci_type, i440fx_host, @@ -253,7 +251,8 @@ static void pc_init1(MachineState *machine, : pc_pci_slot_get_pirq); pcms->bus = pci_bus; - pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, type); + pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, + TYPE_PIIX3_DEVICE); if (xen_enabled()) { pci_device_set_intx_routing_notifier( diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 0549e043ca..117024e450 100644 --- a/hw/isa/piix3.c +++ b/hw/isa/piix3.c @@ -30,7 +30,6 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/isa/isa.h" -#include "hw/xen/xen.h" #include "sysemu/runstate.h" #include "migration/vmstate.h" #include "hw/acpi/acpi_aml_interface.h" @@ -381,24 +380,10 @@ static const TypeInfo piix3_info = { .class_init = piix3_class_init, }; -static void piix3_xen_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = piix3_realize; -} - -static const TypeInfo piix3_xen_info = { - .name = TYPE_PIIX3_XEN_DEVICE, - .parent = TYPE_PIIX3_PCI_DEVICE, - .class_init = piix3_xen_class_init, -}; - static void piix3_register_types(void) { type_register_static(&piix3_pci_type_info); type_register_static(&piix3_info); - type_register_static(&piix3_xen_info); } type_init(piix3_register_types) diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index a840340308..278171752f 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -67,7 +67,6 @@ DECLARE_INSTANCE_CHECKER(PIIX3State, PIIX3_PCI_DEVICE, TYPE_PIIX3_PCI_DEVICE) #define TYPE_PIIX3_DEVICE "PIIX3" -#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen" #define TYPE_PIIX4_PCI_DEVICE "piix4-isa" #endif From 9000666052f99ed4217e75b73636acae61e6fc2c Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Tue, 6 Jun 2023 14:16:05 +0100 Subject: [PATCH 201/412] xen-block: fix segv on unrealize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backtrace: qemu_lockcnt_lock (lockcnt=0xb4) at ../util/lockcnt.c:238 aio_set_fd_handler (ctx=0x0, fd=51, is_external=true, io_read=0x0, io_write=0x0, io_poll=0x0, io_poll_ready=0x0, opaque=0x0) at ../util/aio-posix.c:119 xen_device_unbind_event_channel (xendev=0x55c6da5b5000, channel=0x55c6da6c4c80, errp=0x7fff641ac608) at ../hw/xen/xen-bus.c:926 xen_block_dataplane_stop (dataplane=0x55c6da6ddbe0) at ../hw/block/dataplane/xen-block.c:719 xen_block_disconnect (xendev=0x55c6da5b5000, errp=0x0) at ../hw/block/xen-block.c:48 xen_block_unrealize (xendev=0x55c6da5b5000) at ../hw/block/xen-block.c:154 xen_device_unrealize (dev=0x55c6da5b5000) at ../hw/xen/xen-bus.c:956 xen_device_exit (n=0x55c6da5b50d0, data=0x0) at ../hw/xen/xen-bus.c:985 notifier_list_notify (list=0x55c6d91f9820 , data=0x0) at ../util/notify.c:39 qemu_run_exit_notifiers () at ../softmmu/runstate.c:760 Fixes: f6eac904f682 ("xen-block: implement BlockDevOps->drained_begin()") Signed-off-by: Anthony PERARD Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Message-Id: <20230606131605.55596-1-anthony.perard@citrix.com> Signed-off-by: Anthony PERARD --- hw/xen/xen-bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 1e08cf027a..ece8ec40cd 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -923,8 +923,10 @@ void xen_device_unbind_event_channel(XenDevice *xendev, QLIST_REMOVE(channel, list); - aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), - NULL, NULL, NULL, NULL, NULL); + if (channel->ctx) { + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), + NULL, NULL, NULL, NULL, NULL); + } if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) { error_setg_errno(errp, errno, "xenevtchn_unbind failed"); From c14547580484e40e95e98332a7341da0e46ba2c4 Mon Sep 17 00:00:00 2001 From: Jagannathan Raman Date: Wed, 17 May 2023 11:26:18 -0400 Subject: [PATCH 202/412] vfio-user: update comments Clarify the behavior of TYPE_VFU_OBJECT when TYPE_REMOTE_MACHINE enables the auto-shutdown property. Also, add notes to VFU_OBJECT_ERROR. Signed-off-by: Jagannathan Raman Reviewed-by: Markus Armbruster Reviewed-by: Stefan Hajnoczi --- hw/remote/vfio-user-obj.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 88ffafc73e..8b10c32a3c 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -30,6 +30,11 @@ * * notes - x-vfio-user-server could block IO and monitor during the * initialization phase. + * + * When x-remote machine has the auto-shutdown property + * enabled (default), x-vfio-user-server terminates after the last + * client disconnects. Otherwise, it will continue running until + * explicitly killed. */ #include "qemu/osdep.h" @@ -61,9 +66,12 @@ OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT) /** - * VFU_OBJECT_ERROR - reports an error message. If auto_shutdown - * is set, it aborts the machine on error. Otherwise, it logs an - * error message without aborting. + * VFU_OBJECT_ERROR - reports an error message. + * + * If auto_shutdown is set, it aborts the machine on error. Otherwise, + * it logs an error message without aborting. auto_shutdown is disabled + * when the server serves clients from multiple VMs; as such, an error + * from one VM shouldn't be able to disrupt other VM's services. */ #define VFU_OBJECT_ERROR(o, fmt, ...) \ { \ From 7771e8b86335968ee46538d1afd44246e7a062bc Mon Sep 17 00:00:00 2001 From: Jagannathan Raman Date: Wed, 17 May 2023 11:39:48 -0400 Subject: [PATCH 203/412] docs: fix multi-process QEMU documentation Fix a typo in the system documentation for multi-process QEMU. Signed-off-by: Jagannathan Raman Reviewed-by: Markus Armbruster Reviewed-by: Stefan Hajnoczi --- docs/system/multi-process.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/multi-process.rst b/docs/system/multi-process.rst index 16f0352416..2008a67809 100644 --- a/docs/system/multi-process.rst +++ b/docs/system/multi-process.rst @@ -4,7 +4,7 @@ Multi-process QEMU ================== This document describes how to configure and use multi-process qemu. -For the design document refer to docs/devel/qemu-multiprocess. +For the design document refer to docs/devel/multi-process.rst. 1) Configuration ---------------- From ab64da79774060450046ce8c800eef000024dc8c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 17:52:41 -0700 Subject: [PATCH 204/412] tcg/tci: Adjust passing of MemOpIdx Since adding MO_ATOM_MASK, the maximum MemOpIdx requires 15 bits, which overflows the 12 bit field allocated for TCI memory ops. Expand the field to 16 bits for 2-operand memory ops, and place the value in TCG_REG_TMP for 3-operand memory ops (same as we already do for 4-operand memory ops). Cures a debug assert for aarch64, with FEAT_LSE2 enabled. Signed-off-by: Richard Henderson --- tcg/tci.c | 30 +++++++++++++----------------- tcg/tci/tcg-target.c.inc | 21 ++++----------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/tcg/tci.c b/tcg/tci.c index 813572ff39..4640902c88 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -106,7 +106,7 @@ static void tci_args_rrm(uint32_t insn, TCGReg *r0, { *r0 = extract32(insn, 8, 4); *r1 = extract32(insn, 12, 4); - *m2 = extract32(insn, 20, 12); + *m2 = extract32(insn, 16, 16); } static void tci_args_rrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2) @@ -141,15 +141,6 @@ static void tci_args_rrrc(uint32_t insn, *c3 = extract32(insn, 20, 4); } -static void tci_args_rrrm(uint32_t insn, - TCGReg *r0, TCGReg *r1, TCGReg *r2, MemOpIdx *m3) -{ - *r0 = extract32(insn, 8, 4); - *r1 = extract32(insn, 12, 4); - *r2 = extract32(insn, 16, 4); - *m3 = extract32(insn, 20, 12); -} - static void tci_args_rrrbb(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2, uint8_t *i3, uint8_t *i4) { @@ -929,8 +920,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; } else { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); taddr = tci_uint64(regs[r2], regs[r1]); + oi = regs[r3]; } do_ld_i32: regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr); @@ -941,8 +933,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrm(insn, &r0, &r1, &oi); taddr = (uint32_t)regs[r1]; } else { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); taddr = (uint32_t)regs[r2]; + oi = regs[r3]; } goto do_ld_i64; case INDEX_op_qemu_ld_a64_i64: @@ -972,8 +965,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; } else { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); taddr = tci_uint64(regs[r2], regs[r1]); + oi = regs[r3]; } do_st_i32: tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr); @@ -985,9 +979,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tmp64 = regs[r0]; taddr = (uint32_t)regs[r1]; } else { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); tmp64 = tci_uint64(regs[r1], regs[r0]); taddr = (uint32_t)regs[r2]; + oi = regs[r3]; } goto do_st_i64; case INDEX_op_qemu_st_a64_i64: @@ -1293,9 +1288,10 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), oi); break; case 3: - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %x", - op_name, str_r(r0), str_r(r1), str_r(r2), oi); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", + op_name, str_r(r0), str_r(r1), + str_r(r2), str_r(r3)); break; case 4: tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index c9516a5e8b..5b456e1277 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -331,11 +331,11 @@ static void tcg_out_op_rrm(TCGContext *s, TCGOpcode op, { tcg_insn_unit insn = 0; - tcg_debug_assert(m2 == extract32(m2, 0, 12)); + tcg_debug_assert(m2 == extract32(m2, 0, 16)); insn = deposit32(insn, 0, 8, op); insn = deposit32(insn, 8, 4, r0); insn = deposit32(insn, 12, 4, r1); - insn = deposit32(insn, 20, 12, m2); + insn = deposit32(insn, 16, 16, m2); tcg_out32(s, insn); } @@ -392,20 +392,6 @@ static void tcg_out_op_rrrc(TCGContext *s, TCGOpcode op, tcg_out32(s, insn); } -static void tcg_out_op_rrrm(TCGContext *s, TCGOpcode op, - TCGReg r0, TCGReg r1, TCGReg r2, TCGArg m3) -{ - tcg_insn_unit insn = 0; - - tcg_debug_assert(m3 == extract32(m3, 0, 12)); - insn = deposit32(insn, 0, 8, op); - insn = deposit32(insn, 8, 4, r0); - insn = deposit32(insn, 12, 4, r1); - insn = deposit32(insn, 16, 4, r2); - insn = deposit32(insn, 20, 12, m3); - tcg_out32(s, insn); -} - static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1, TCGReg r2, uint8_t b3, uint8_t b4) { @@ -860,7 +846,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, if (TCG_TARGET_REG_BITS == 64) { tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); } else { - tcg_out_op_rrrm(s, opc, args[0], args[1], args[2], args[3]); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[4]); + tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); } break; case INDEX_op_qemu_ld_a64_i64: From 0cabaef3ed13697e2da0ceb18b3da9e21d0b4d83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 18:06:15 -0700 Subject: [PATCH 205/412] tcg/tci: Adjust call-clobbered regs for int128_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We require either 2 or 4 registers to hold int128_t. Failure to do so results in a register allocation assert. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tci/tcg-target.c.inc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 5b456e1277..0037f904f1 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -179,8 +179,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) } static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R2, - TCG_REG_R3, TCG_REG_R4, TCG_REG_R5, TCG_REG_R6, @@ -193,6 +191,9 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R13, TCG_REG_R14, TCG_REG_R15, + /* Either 2 or 4 of these are call clobbered, so use them last. */ + TCG_REG_R3, + TCG_REG_R2, TCG_REG_R1, TCG_REG_R0, }; @@ -934,11 +935,11 @@ static void tcg_target_init(TCGContext *s) /* * The interpreter "registers" are in the local stack frame and * cannot be clobbered by the called helper functions. However, - * the interpreter assumes a 64-bit return value and assigns to + * the interpreter assumes a 128-bit return value and assigns to * the return value registers. */ tcg_target_call_clobber_regs = - MAKE_64BIT_MASK(TCG_REG_R0, 64 / TCG_TARGET_REG_BITS); + MAKE_64BIT_MASK(TCG_REG_R0, 128 / TCG_TARGET_REG_BITS); s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); From 007cd176e590c77e91d1531ec5acbe86b15b0f00 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 19:42:58 +0000 Subject: [PATCH 206/412] target/arm: Only include tcg/oversized-guest.h if CONFIG_TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the build for --disable-tcg. This header is only needed for cross-hosting. Without CONFIG_TCG, we know this is an AArch64 host, CONFIG_ATOMIC64 will be set, and the TCG_OVERSIZED_GUEST block will never be compiled. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/ptw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index b2dc223525..37bcb17a9e 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -14,8 +14,9 @@ #include "cpu.h" #include "internals.h" #include "idau.h" -#include "tcg/oversized-guest.h" - +#ifdef CONFIG_TCG +# include "tcg/oversized-guest.h" +#endif typedef struct S1Translate { ARMMMUIdx in_mmu_idx; From c81e2d5477c92b4a96c779bffbba3dddb23b91be Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 19:53:35 +0000 Subject: [PATCH 207/412] gitlab: Add cross-arm64-kvm-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are not currently running a --disable-tcg test for arm64, like we are for mips, ppc and s390x. We have a job for the native aarch64 runner, but it is not run by default and it is not helpful for normal developer testing without access to qemu's private runner. Use --without-default-features to eliminate most tests. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson --- .gitlab-ci.d/crossbuilds.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 61b8ac86ee..1e0e6c7f2c 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -29,6 +29,14 @@ cross-arm64-user: variables: IMAGE: debian-arm64-cross +cross-arm64-kvm-only: + extends: .cross_accel_build_job + needs: + job: arm64-debian-cross-container + variables: + IMAGE: debian-arm64-cross + EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features + cross-i386-user: extends: - .cross_user_build_job From dcc28ab603f30df5cc8be1f759b423e94ae7d10f Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 7 Jun 2023 17:36:06 +0300 Subject: [PATCH 208/412] iotests: fix 194: filter out racy postcopy-active event The event is racy: it will not appear in the output if bitmap is migrated during downtime period of migration and postcopy phase is not started. Fixes: ae00aa239847 "iotests: 194: test also migration of dirty bitmap" Reported-by: Richard Henderson Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20230607143606.1557395-1-vsementsov@yandex-team.ru> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- tests/qemu-iotests/194 | 5 +++++ tests/qemu-iotests/194.out | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index 68894371f5..c0ce82dd25 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -74,6 +74,11 @@ with iotests.FilePath('source.img') as source_img_path, \ while True: event1 = source_vm.event_wait('MIGRATION') + if event1['data']['status'] == 'postcopy-active': + # This event is racy, it depends do we really do postcopy or bitmap + # was migrated during downtime (and no data to migrate in postcopy + # phase). So, don't log it. + continue iotests.log(event1, filters=[iotests.filter_qmp_event]) if event1['data']['status'] in ('completed', 'failed'): iotests.log('Gracefully ending the `drive-mirror` job on source...') diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 4e6df1565a..376ed1d2e6 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -14,7 +14,6 @@ Starting migration... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"data": {"status": "postcopy-active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Gracefully ending the `drive-mirror` job on source... {"return": {}} From 8b50d564bee4d0e3864456ac8e92f981dba7959a Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 26 May 2023 08:19:41 +0200 Subject: [PATCH 209/412] tests/tcg/tricore: Move asm tests into 'asm' directory this seperates these tests from the upcoming tests written in C. Also rename the compiled test to 'test_.asm.tst'. Signed-off-by: Bastian Koppelmann Message-Id: <20230526061946.54514-2-kbastian@mail.uni-paderborn.de> --- tests/tcg/tricore/Makefile.softmmu-target | 35 ++++++++++++----------- tests/tcg/tricore/{ => asm}/macros.h | 0 tests/tcg/tricore/{ => asm}/test_abs.S | 0 tests/tcg/tricore/{ => asm}/test_bmerge.S | 0 tests/tcg/tricore/{ => asm}/test_clz.S | 0 tests/tcg/tricore/{ => asm}/test_dextr.S | 0 tests/tcg/tricore/{ => asm}/test_dvstep.S | 0 tests/tcg/tricore/{ => asm}/test_fadd.S | 0 tests/tcg/tricore/{ => asm}/test_fmul.S | 0 tests/tcg/tricore/{ => asm}/test_ftoi.S | 0 tests/tcg/tricore/{ => asm}/test_imask.S | 0 tests/tcg/tricore/{ => asm}/test_insert.S | 0 tests/tcg/tricore/{ => asm}/test_ld_bu.S | 0 tests/tcg/tricore/{ => asm}/test_ld_h.S | 0 tests/tcg/tricore/{ => asm}/test_madd.S | 0 tests/tcg/tricore/{ => asm}/test_msub.S | 0 tests/tcg/tricore/{ => asm}/test_muls.S | 0 17 files changed, 18 insertions(+), 17 deletions(-) rename tests/tcg/tricore/{ => asm}/macros.h (100%) rename tests/tcg/tricore/{ => asm}/test_abs.S (100%) rename tests/tcg/tricore/{ => asm}/test_bmerge.S (100%) rename tests/tcg/tricore/{ => asm}/test_clz.S (100%) rename tests/tcg/tricore/{ => asm}/test_dextr.S (100%) rename tests/tcg/tricore/{ => asm}/test_dvstep.S (100%) rename tests/tcg/tricore/{ => asm}/test_fadd.S (100%) rename tests/tcg/tricore/{ => asm}/test_fmul.S (100%) rename tests/tcg/tricore/{ => asm}/test_ftoi.S (100%) rename tests/tcg/tricore/{ => asm}/test_imask.S (100%) rename tests/tcg/tricore/{ => asm}/test_insert.S (100%) rename tests/tcg/tricore/{ => asm}/test_ld_bu.S (100%) rename tests/tcg/tricore/{ => asm}/test_ld_h.S (100%) rename tests/tcg/tricore/{ => asm}/test_madd.S (100%) rename tests/tcg/tricore/{ => asm}/test_msub.S (100%) rename tests/tcg/tricore/{ => asm}/test_muls.S (100%) diff --git a/tests/tcg/tricore/Makefile.softmmu-target b/tests/tcg/tricore/Makefile.softmmu-target index 49e573bc3b..29c75acfb3 100644 --- a/tests/tcg/tricore/Makefile.softmmu-target +++ b/tests/tcg/tricore/Makefile.softmmu-target @@ -1,33 +1,34 @@ TESTS_PATH = $(SRC_PATH)/tests/tcg/tricore +ASM_TESTS_PATH = $(TESTS_PATH)/asm LDFLAGS = -T$(TESTS_PATH)/link.ld --mcpu=tc162 ASFLAGS = -mtc162 -TESTS += test_abs.tst -TESTS += test_bmerge.tst -TESTS += test_clz.tst -TESTS += test_dextr.tst -TESTS += test_dvstep.tst -TESTS += test_fadd.tst -TESTS += test_fmul.tst -TESTS += test_ftoi.tst -TESTS += test_imask.tst -TESTS += test_insert.tst -TESTS += test_ld_bu.tst -TESTS += test_ld_h.tst -TESTS += test_madd.tst -TESTS += test_msub.tst -TESTS += test_muls.tst +TESTS += test_abs.asm.tst +TESTS += test_bmerge.asm.tst +TESTS += test_clz.asm.tst +TESTS += test_dextr.asm.tst +TESTS += test_dvstep.asm.tst +TESTS += test_fadd.asm.tst +TESTS += test_fmul.asm.tst +TESTS += test_ftoi.asm.tst +TESTS += test_imask.asm.tst +TESTS += test_insert.asm.tst +TESTS += test_ld_bu.asm.tst +TESTS += test_ld_h.asm.tst +TESTS += test_madd.asm.tst +TESTS += test_msub.asm.tst +TESTS += test_muls.asm.tst QEMU_OPTS += -M tricore_testboard -cpu tc27x -nographic -kernel -%.pS: $(TESTS_PATH)/%.S +%.pS: $(ASM_TESTS_PATH)/%.S $(HOST_CC) -E -o $@ $< %.o: %.pS $(AS) $(ASFLAGS) -o $@ $< -%.tst: %.o +%.asm.tst: %.o $(LD) $(LDFLAGS) $< -o $@ # We don't currently support the multiarch system tests diff --git a/tests/tcg/tricore/macros.h b/tests/tcg/tricore/asm/macros.h similarity index 100% rename from tests/tcg/tricore/macros.h rename to tests/tcg/tricore/asm/macros.h diff --git a/tests/tcg/tricore/test_abs.S b/tests/tcg/tricore/asm/test_abs.S similarity index 100% rename from tests/tcg/tricore/test_abs.S rename to tests/tcg/tricore/asm/test_abs.S diff --git a/tests/tcg/tricore/test_bmerge.S b/tests/tcg/tricore/asm/test_bmerge.S similarity index 100% rename from tests/tcg/tricore/test_bmerge.S rename to tests/tcg/tricore/asm/test_bmerge.S diff --git a/tests/tcg/tricore/test_clz.S b/tests/tcg/tricore/asm/test_clz.S similarity index 100% rename from tests/tcg/tricore/test_clz.S rename to tests/tcg/tricore/asm/test_clz.S diff --git a/tests/tcg/tricore/test_dextr.S b/tests/tcg/tricore/asm/test_dextr.S similarity index 100% rename from tests/tcg/tricore/test_dextr.S rename to tests/tcg/tricore/asm/test_dextr.S diff --git a/tests/tcg/tricore/test_dvstep.S b/tests/tcg/tricore/asm/test_dvstep.S similarity index 100% rename from tests/tcg/tricore/test_dvstep.S rename to tests/tcg/tricore/asm/test_dvstep.S diff --git a/tests/tcg/tricore/test_fadd.S b/tests/tcg/tricore/asm/test_fadd.S similarity index 100% rename from tests/tcg/tricore/test_fadd.S rename to tests/tcg/tricore/asm/test_fadd.S diff --git a/tests/tcg/tricore/test_fmul.S b/tests/tcg/tricore/asm/test_fmul.S similarity index 100% rename from tests/tcg/tricore/test_fmul.S rename to tests/tcg/tricore/asm/test_fmul.S diff --git a/tests/tcg/tricore/test_ftoi.S b/tests/tcg/tricore/asm/test_ftoi.S similarity index 100% rename from tests/tcg/tricore/test_ftoi.S rename to tests/tcg/tricore/asm/test_ftoi.S diff --git a/tests/tcg/tricore/test_imask.S b/tests/tcg/tricore/asm/test_imask.S similarity index 100% rename from tests/tcg/tricore/test_imask.S rename to tests/tcg/tricore/asm/test_imask.S diff --git a/tests/tcg/tricore/test_insert.S b/tests/tcg/tricore/asm/test_insert.S similarity index 100% rename from tests/tcg/tricore/test_insert.S rename to tests/tcg/tricore/asm/test_insert.S diff --git a/tests/tcg/tricore/test_ld_bu.S b/tests/tcg/tricore/asm/test_ld_bu.S similarity index 100% rename from tests/tcg/tricore/test_ld_bu.S rename to tests/tcg/tricore/asm/test_ld_bu.S diff --git a/tests/tcg/tricore/test_ld_h.S b/tests/tcg/tricore/asm/test_ld_h.S similarity index 100% rename from tests/tcg/tricore/test_ld_h.S rename to tests/tcg/tricore/asm/test_ld_h.S diff --git a/tests/tcg/tricore/test_madd.S b/tests/tcg/tricore/asm/test_madd.S similarity index 100% rename from tests/tcg/tricore/test_madd.S rename to tests/tcg/tricore/asm/test_madd.S diff --git a/tests/tcg/tricore/test_msub.S b/tests/tcg/tricore/asm/test_msub.S similarity index 100% rename from tests/tcg/tricore/test_msub.S rename to tests/tcg/tricore/asm/test_msub.S diff --git a/tests/tcg/tricore/test_muls.S b/tests/tcg/tricore/asm/test_muls.S similarity index 100% rename from tests/tcg/tricore/test_muls.S rename to tests/tcg/tricore/asm/test_muls.S From 2b8e2992c3c9aa6bee3dd23e2137a0923f7dc0aa Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 26 May 2023 08:19:42 +0200 Subject: [PATCH 210/412] tests/tcg/tricore: Uses label for memory addresses the linker might rearrange sections, so lets reference memory by label name instead of addr + off. Signed-off-by: Bastian Koppelmann Message-Id: <20230526061946.54514-3-kbastian@mail.uni-paderborn.de> --- tests/tcg/tricore/asm/macros.h | 1 - tests/tcg/tricore/asm/test_ld_bu.S | 4 ++-- tests/tcg/tricore/asm/test_ld_h.S | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/tcg/tricore/asm/macros.h b/tests/tcg/tricore/asm/macros.h index 3df2e0de82..b5087b5c97 100644 --- a/tests/tcg/tricore/asm/macros.h +++ b/tests/tcg/tricore/asm/macros.h @@ -25,7 +25,6 @@ #define AREG_ADDR %a0 #define AREG_CORRECT_RESULT %a3 -#define MEM_BASE_ADDR 0xd0000000 #define DREG_DEV_ADDR %a15 diff --git a/tests/tcg/tricore/asm/test_ld_bu.S b/tests/tcg/tricore/asm/test_ld_bu.S index ff9dac128b..4a1f40c37b 100644 --- a/tests/tcg/tricore/asm/test_ld_bu.S +++ b/tests/tcg/tricore/asm/test_ld_bu.S @@ -9,7 +9,7 @@ _start: # expect. addr reg val after load # insn num expect. load value | pattern for loading # | | | | | - TEST_LD(ld.bu, 1, 0xff, MEM_BASE_ADDR + 4, [+AREG_ADDR]4) # pre_inc - TEST_LD(ld.bu, 2, 0xad, MEM_BASE_ADDR + 4, [AREG_ADDR+]4) # post_inc + TEST_LD(ld.bu, 1, 0xff, test_data + 4, [+AREG_ADDR]4) # pre_inc + TEST_LD(ld.bu, 2, 0xad, test_data + 4, [AREG_ADDR+]4) # post_inc TEST_PASSFAIL diff --git a/tests/tcg/tricore/asm/test_ld_h.S b/tests/tcg/tricore/asm/test_ld_h.S index d3c157a046..f5e4959198 100644 --- a/tests/tcg/tricore/asm/test_ld_h.S +++ b/tests/tcg/tricore/asm/test_ld_h.S @@ -7,9 +7,9 @@ test_data: .global _start _start: # expect. addr reg val after load -# insn num expect. load value | pattern for loading -# | | | | | - TEST_LD (ld.h, 1, 0xffffaffe, MEM_BASE_ADDR, [AREG_ADDR]2) - TEST_LD_SRO(ld.h, 2, 0x000022ff, MEM_BASE_ADDR, [AREG_ADDR]4) +# insn num expect. load value | pattern for loading +# | | | | | + TEST_LD (ld.h, 1, 0xffffaffe, test_data, [AREG_ADDR]2) + TEST_LD_SRO(ld.h, 2, 0x000022ff, test_data, [AREG_ADDR]4) TEST_PASSFAIL From 0e45f7beca959ca085fef3439fa39d2588bc6f52 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 26 May 2023 08:19:43 +0200 Subject: [PATCH 211/412] tests/tcg/tricore: Add first C program this allows us to exercise the startup code used by GCC to call main(). Signed-off-by: Bastian Koppelmann Message-Id: <20230526061946.54514-4-kbastian@mail.uni-paderborn.de> --- configure | 1 + tests/tcg/tricore/Makefile.softmmu-target | 13 + tests/tcg/tricore/c/crt0-tc2x.S | 335 ++++++++++++++++++++++ tests/tcg/tricore/c/test_boot_to_main.c | 13 + tests/tcg/tricore/c/testdev_assert.h | 18 ++ tests/tcg/tricore/link.ld | 16 ++ 6 files changed, 396 insertions(+) create mode 100644 tests/tcg/tricore/c/crt0-tc2x.S create mode 100644 tests/tcg/tricore/c/test_boot_to_main.c create mode 100644 tests/tcg/tricore/c/testdev_assert.h diff --git a/configure b/configure index 8765b88e12..768e674633 100755 --- a/configure +++ b/configure @@ -1383,6 +1383,7 @@ probe_target_compiler() { container_cross_prefix=tricore- container_cross_as=tricore-as container_cross_ld=tricore-ld + container_cross_cc=tricore-gcc break ;; x86_64) diff --git a/tests/tcg/tricore/Makefile.softmmu-target b/tests/tcg/tricore/Makefile.softmmu-target index 29c75acfb3..f051444991 100644 --- a/tests/tcg/tricore/Makefile.softmmu-target +++ b/tests/tcg/tricore/Makefile.softmmu-target @@ -1,8 +1,10 @@ TESTS_PATH = $(SRC_PATH)/tests/tcg/tricore ASM_TESTS_PATH = $(TESTS_PATH)/asm +C_TESTS_PATH = $(TESTS_PATH)/c LDFLAGS = -T$(TESTS_PATH)/link.ld --mcpu=tc162 ASFLAGS = -mtc162 +CFLAGS = -mtc162 -c TESTS += test_abs.asm.tst TESTS += test_bmerge.asm.tst @@ -20,6 +22,8 @@ TESTS += test_madd.asm.tst TESTS += test_msub.asm.tst TESTS += test_muls.asm.tst +TESTS += test_boot_to_main.c.tst + QEMU_OPTS += -M tricore_testboard -cpu tc27x -nographic -kernel %.pS: $(ASM_TESTS_PATH)/%.S @@ -31,5 +35,14 @@ QEMU_OPTS += -M tricore_testboard -cpu tc27x -nographic -kernel %.asm.tst: %.o $(LD) $(LDFLAGS) $< -o $@ +crt0-tc2x.o: $(C_TESTS_PATH)/crt0-tc2x.S + $(AS) $(ASFLAGS) -o $@ $< + +%.o: $(C_TESTS_PATH)/%.c + $(CC) $(CFLAGS) -o $@ $< + +%.c.tst: %.o crt0-tc2x.o + $(LD) $(LDFLAGS) -o $@ $^ + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/tricore/c/crt0-tc2x.S b/tests/tcg/tricore/c/crt0-tc2x.S new file mode 100644 index 0000000000..3100da123c --- /dev/null +++ b/tests/tcg/tricore/c/crt0-tc2x.S @@ -0,0 +1,335 @@ +/* + * crt0-tc2x.S -- Startup code for GNU/TriCore applications. + * + * Copyright (C) 1998-2014 HighTec EDV-Systeme GmbH. + * + * This file is part of GCC. + * + * GCC 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. + * + * GCC 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. + * + * Under Section 7 of GPL version 3, you are granted additional + * permissions described in the GCC Runtime Library Exception, version + * 3.1, as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License and + * a copy of the GCC Runtime Library Exception along with this program; + * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + * . */ + +/* Define the Derivate Name as a hexvalue. This value + * is built-in defined in tricore-c.c (from tricore-devices.c) + * the derivate number as a hexvalue (e.g. TC1796 => 0x1796 + * This name will be used in the memory.x Memory description to + * to confirm that the crt0.o and the memory.x will be get from + * same directory + */ + .section ".startup_code", "ax", @progbits + .global _start + .type _start,@function + +/* default BMI header (only TC2xxx devices) */ + .word 0x00000000 + .word 0xb3590070 + .word 0x00000000 + .word 0x00000000 + .word 0x00000000 + .word 0x00000000 + .word 0x791eb864 + .word 0x86e1479b + +_start: + .code32 + j _startaddr + .align 2 + +_startaddr: + /* + * initialize user and interrupt stack pointers + */ + movh.a %sp,hi:__USTACK # load %sp + lea %sp,[%sp]lo:__USTACK + movh %d0,hi:__ISTACK # load $isp + addi %d0,%d0,lo:__ISTACK + mtcr $isp,%d0 + isync + +#; install trap handlers + + movh %d0,hi:first_trap_table #; load $btv + addi %d0,%d0,lo:first_trap_table + mtcr $btv,%d0 + isync + + /* + * initialize call depth counter + */ + + mfcr %d0,$psw + or %d0,%d0,0x7f # disable call depth counting + andn %d0,%d0,0x80 # clear CDE bit + mtcr $psw,%d0 + isync + + /* + * initialize access to system global registers + */ + + mfcr %d0,$psw + or %d0,%d0,0x100 # set GW bit + mtcr $psw,%d0 + isync + + /* + * initialize SDA base pointers + */ + .global _SMALL_DATA_,_SMALL_DATA2_,_SMALL_DATA3_,_SMALL_DATA4_ + .weak _SMALL_DATA_,_SMALL_DATA2_,_SMALL_DATA3_,_SMALL_DATA4_ + + movh.a %a0,hi:_SMALL_DATA_ # %a0 addresses .sdata/.sbss + lea %a0,[%a0]lo:_SMALL_DATA_ + movh.a %a1,hi:_SMALL_DATA2_ # %a1 addresses .sdata2/.sbss2 + lea %a1,[%a1]lo:_SMALL_DATA2_ + movh.a %a8,hi:_SMALL_DATA3_ # %a8 addresses .sdata3/.sbss3 + lea %a8,[%a8]lo:_SMALL_DATA3_ + movh.a %a9,hi:_SMALL_DATA4_ # %a9 addresses .sdata4/.sbss4 + lea %a9,[%a9]lo:_SMALL_DATA4_ + + /* + * reset access to system global registers + */ + + mfcr %d0,$psw + andn %d0,%d0,0x100 # clear GW bit + mtcr $psw,%d0 + isync + + /* + * initialize context save areas + */ + + jl __init_csa + + + + /* + * handle clear table (i.e., fill BSS with zeros) + */ + + jl __clear_table_func + + + /* + * handle copy table (support for romable code) + */ + + jl __copy_table_func + + + /* + * _exit (main (0, NULL)); + */ + mov %d4,0 # argc = 0 + sub.a %sp,8 + st.w [%sp]0,%d4 + st.w [%sp]4,%d4 + mov.aa %a4,%sp # argv + + call main # int retval = main (0, NULL); + mov.a %a14,%d2 # move exit code to match trap handler + j _exit # _exit (retval); + + debug # should never come here + + + /* + * initialize context save areas (CSAs), PCXI, LCX and FCX + */ + + .global __init_csa + .type __init_csa,function + +__init_csa: + movh %d0,0 + mtcr $pcxi,%d0 + isync + movh %d0,hi:__CSA_BEGIN #; %d0 = begin of CSA + addi %d0,%d0,lo:__CSA_BEGIN + addi %d0,%d0,63 #; force alignment (2^6) + andn %d0,%d0,63 + movh %d2,hi:__CSA_END #; %d2 = end of CSA + addi %d2,%d2,lo:__CSA_END + andn %d2,%d2,63 #; force alignment (2^6) + sub %d2,%d2,%d0 + sh %d2,%d2,-6 #; %d2 = number of CSAs + mov.a %a3,%d0 #; %a3 = address of first CSA + extr.u %d0,%d0,28,4 #; %d0 = segment << 16 + sh %d0,%d0,16 + lea %a4,0 #; %a4 = previous CSA = 0 + st.a [%a3],%a4 #; store it in 1st CSA + mov.aa %a4,%a3 #; %a4 = current CSA + lea %a3,[%a3]64 #; %a3 = %a3->nextCSA + mov.d %d1,%a3 + extr.u %d1,%d1,6,16 #; get CSA index + or %d1,%d1,%d0 #; add segment number + mtcr $lcx,%d1 #; initialize LCX + add %d2,%d2,-2 #; CSAs to initialize -= 2 + mov.a %a5,%d2 #; %a5 = loop counter +csa_loop: + mov.d %d1,%a4 #; %d1 = current CSA address + extr.u %d1,%d1,6,16 #; get CSA index + or %d1,%d1,%d0 #; add segment number + st.w [%a3],%d1 #; store "nextCSA" pointer + mov.aa %a4,%a3 #; %a4 = current CSA address + lea %a3,[%a3]64 #; %a3 = %a3->nextCSA + loop %a5,csa_loop #; repeat until done + + mov.d %d1,%a4 #; %d1 = current CSA address + extr.u %d1,%d1,6,16 #; get CSA index + or %d1,%d1,%d0 #; add segment number + mtcr $fcx,%d1 #; initialize FCX + isync + ji %a11 + + + + + /* + * handle clear table (i.e., fill BSS with zeros) + */ + .global __clear_table_func + .type __clear_table_func,@function + +__clear_table_func: + mov %d14,0 # %e14 = 0 + mov %d15,0 + movh.a %a13,hi:__clear_table # %a13 = &first table entry + lea %a13,[%a13]lo:__clear_table + +__clear_table_next: + ld.a %a15,[%a13+]4 # %a15 = current block base + ld.w %d3,[%a13+]4 # %d3 = current block length + jeq %d3,-1,__clear_table_done # length == -1 => end of table + sh %d0,%d3,-3 # %d0 = length / 8 (doublewords) + and %d1,%d3,7 # %d1 = length % 8 (rem. bytes) + jz %d0,__clear_word # block size < 8 => clear word + addi %d0,%d0,-1 # else doublewords -= 1 + mov.a %a2,%d0 # %a2 = loop counter +__clear_dword: + st.d [%a15+]8,%e14 # clear one doubleword + loop %a2,__clear_dword +__clear_word: + jz %d1,__clear_table_next + sh %d0,%d1,-2 # %d0 = length / 4 (words) + and %d1,%d1,3 # %d1 = length % 4 (rem. bytes) + jz %d0,__clear_hword # block size < 4 => clear hword + st.w [%a15+]4,%d15 # clear one word +__clear_hword: + jz %d1,__clear_table_next + sh %d0,%d1,-1 # %d0 = length / 2 (halfwords) + and %d1,%d1,1 # %d1 = length % 2 (rem. bytes) + jz %d0,__clear_byte # block size < 2 => clear byte + st.h [%a15+]2,%d15 # clear one halfword +__clear_byte: + jz %d1,__clear_table_next + st.b [%a15],%d15 # clear one byte + j __clear_table_next # handle next clear table entry +__clear_table_done: + + ji %a11 + + + + /* + * handle copy table (support for romable code) + */ + .global __copy_table_func + .type __copy_table_func,@function + +__copy_table_func: + movh.a %a13,hi:__copy_table # %a13 = &first table entry + lea %a13,[%a13]lo:__copy_table + +__copy_table_next: + ld.a %a15,[%a13+]4 # %a15 = src address + ld.a %a14,[%a13+]4 # %a14 = dst address + ld.w %d3,[%a13+]4 # %d3 = block length + jeq %d3,-1,__copy_table_done # length == -1 => end of table + sh %d0,%d3,-3 # %d0 = length / 8 (doublewords) + and %d1,%d3,7 # %d1 = lenght % 8 (rem. bytes) + jz %d0,__copy_word # block size < 8 => copy word + addi %d0,%d0,-1 # else doublewords -= 1 + mov.a %a2,%d0 # %a2 = loop counter +__copy_dword: + ld.d %e14,[%a15+]8 # copy one doubleword + st.d [%a14+]8,%e14 + loop %a2,__copy_dword +__copy_word: + jz %d1,__copy_table_next + sh %d0,%d1,-2 # %d0 = length / 4 (words) + and %d1,%d1,3 # %d1 = lenght % 4 (rem. bytes) + jz %d0,__copy_hword # block size < 4 => copy hword + ld.w %d14,[%a15+]4 # copy one word + st.w [%a14+]4,%d14 +__copy_hword: + jz %d1,__copy_table_next + sh %d0,%d1,-1 # %d0 = length / 2 (halfwords) + and %d1,%d1,1 # %d1 = length % 2 (rem. bytes) + jz %d0,__copy_byte # block size < 2 => copy byte + ld.h %d14,[%a15+]2 # copy one halfword + st.h [%a14+]2,%d14 +__copy_byte: + jz %d1,__copy_table_next + ld.b %d14,[%a15]0 # copy one byte + st.b [%a14],%d14 + j __copy_table_next # handle next copy table entry +__copy_table_done: + + ji %a11 + +_exit: + movh.a %a15, hi:__TESTDEVICE + lea %a15,[%a15]lo:__TESTDEVICE + mov.d %d2, %a14 + st.w [%a15], %d2 # write exit code to testdevice + debug + +/*============================================================================* + * Exception handlers (exceptions in startup code) + * + * This is a minimal trap vector table, which consists of eight + * entries, each consisting of eight words (32 bytes). + *============================================================================*/ + + +#; .section .traptab, "ax", @progbits + +.macro trapentry from=0, to=7 + mov.u %d14, \from << 8 + add %d14,%d14,%d15 + mov.a %a14,%d14 + addih.a %a14,%a14,0 # if we trap, we fail + j _exit +0: + j 0b + nop + rfe + .align 5 + + .if \to-\from + trapentry "(\from+1)",\to + .endif +.endm + + .align 8 + .global first_trap_table +first_trap_table: + trapentry 0, 7 + diff --git a/tests/tcg/tricore/c/test_boot_to_main.c b/tests/tcg/tricore/c/test_boot_to_main.c new file mode 100644 index 0000000000..fa28a5b433 --- /dev/null +++ b/tests/tcg/tricore/c/test_boot_to_main.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2023 Bastian Koppelmann + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "testdev_assert.h" +int main(int argc, char **argv) +{ + testdev_assert(1); + return 0; +} diff --git a/tests/tcg/tricore/c/testdev_assert.h b/tests/tcg/tricore/c/testdev_assert.h new file mode 100644 index 0000000000..ccd14f5025 --- /dev/null +++ b/tests/tcg/tricore/c/testdev_assert.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2023 Bastian Koppelmann + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +int *testdev = (int *)0xf0000000; + +#define FAIL 1 +static inline void testdev_assert(int condition) +{ + if (!condition) { + *testdev = FAIL; + asm("debug"); + } +} + diff --git a/tests/tcg/tricore/link.ld b/tests/tcg/tricore/link.ld index 364bcdc00a..acc1758c41 100644 --- a/tests/tcg/tricore/link.ld +++ b/tests/tcg/tricore/link.ld @@ -12,6 +12,7 @@ MEMORY /* * Define the sizes of the user and system stacks. */ +__ISTACK_SIZE = DEFINED (__ISTACK_SIZE) ? __ISTACK_SIZE : 256 ; __USTACK_SIZE = DEFINED (__USTACK_SIZE) ? __USTACK_SIZE : 1K ; /* * Define the start address and the size of the context save area. @@ -20,6 +21,8 @@ __CSA_BEGIN = 0xd0000000 ; __CSA_SIZE = 8k ; __CSA_END = __CSA_BEGIN + __CSA_SIZE ; +__TESTDEVICE = 0xf0000000 ; + SECTIONS { .text : @@ -32,6 +35,18 @@ SECTIONS { *(.rodata) *(.rodata1) + /* + * Create the clear and copy tables that tell the startup code + * which memory areas to clear and to copy, respectively. + */ + . = ALIGN(4) ; + PROVIDE(__clear_table = .) ; + LONG(0 + ADDR(.bss)); LONG(SIZEOF(.bss)); + LONG(-1); LONG(-1); + PROVIDE(__copy_table = .) ; + LONG(LOADADDR(.data)); LONG(0 + ADDR(.data)); LONG(SIZEOF(.data)); + LONG(-1); LONG(-1); LONG(-1); + . = ALIGN(8); } > data_ram .data : @@ -40,6 +55,7 @@ SECTIONS *(.data) *(.data.*) . = ALIGN(8) ; + __ISTACK = . + __ISTACK_SIZE ; __USTACK = . + __USTACK_SIZE -768; } > data_ram From 343cdf2c9ad6aef2fbf66cf9c888d20c1de5e784 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 26 May 2023 08:19:44 +0200 Subject: [PATCH 212/412] target/tricore: Refactor PCXI/ICR register fields starting from ISA version 1.6.1 (previously known as 1.6P/E), some bitfields in PCXI and ICR have changed. We also refactor these registers using the register fields API. Signed-off-by: Bastian Koppelmann Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1453 Message-Id: <20230526061946.54514-5-kbastian@mail.uni-paderborn.de> --- target/tricore/cpu.h | 37 +++++++++++----- target/tricore/helper.c | 45 ++++++++++++++++++++ target/tricore/op_helper.c | 87 +++++++++++++++++++------------------- target/tricore/translate.c | 10 ++++- 4 files changed, 123 insertions(+), 56 deletions(-) diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index 47d0ffb745..d98a3fb671 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -21,6 +21,7 @@ #define TRICORE_CPU_H #include "cpu-qom.h" +#include "hw/registerfields.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "tricore-defs.h" @@ -199,13 +200,33 @@ struct ArchCPU { hwaddr tricore_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void tricore_cpu_dump_state(CPUState *cpu, FILE *f, int flags); +FIELD(PCXI, PCPN_13, 24, 8) +FIELD(PCXI, PCPN_161, 22, 8) +FIELD(PCXI, PIE_13, 23, 1) +FIELD(PCXI, PIE_161, 21, 1) +FIELD(PCXI, UL_13, 22, 1) +FIELD(PCXI, UL_161, 20, 1) +FIELD(PCXI, PCXS, 16, 4) +FIELD(PCXI, PCXO, 0, 16) +uint32_t pcxi_get_ul(CPUTriCoreState *env); +uint32_t pcxi_get_pie(CPUTriCoreState *env); +uint32_t pcxi_get_pcpn(CPUTriCoreState *env); +uint32_t pcxi_get_pcxs(CPUTriCoreState *env); +uint32_t pcxi_get_pcxo(CPUTriCoreState *env); +void pcxi_set_ul(CPUTriCoreState *env, uint32_t val); +void pcxi_set_pie(CPUTriCoreState *env, uint32_t val); +void pcxi_set_pcpn(CPUTriCoreState *env, uint32_t val); -#define MASK_PCXI_PCPN 0xff000000 -#define MASK_PCXI_PIE_1_3 0x00800000 -#define MASK_PCXI_PIE_1_6 0x00200000 -#define MASK_PCXI_UL 0x00400000 -#define MASK_PCXI_PCXS 0x000f0000 -#define MASK_PCXI_PCXO 0x0000ffff +FIELD(ICR, IE_161, 15, 1) +FIELD(ICR, IE_13, 8, 1) +FIELD(ICR, PIPN, 16, 8) +FIELD(ICR, CCPN, 0, 8) + +uint32_t icr_get_ie(CPUTriCoreState *env); +uint32_t icr_get_ccpn(CPUTriCoreState *env); + +void icr_set_ccpn(CPUTriCoreState *env, uint32_t val); +void icr_set_ie(CPUTriCoreState *env, uint32_t val); #define MASK_PSW_USB 0xff000000 #define MASK_USB_C 0x80000000 @@ -228,10 +249,6 @@ void tricore_cpu_dump_state(CPUState *cpu, FILE *f, int flags); #define MASK_CPUID_MOD_32B 0x0000ff00 #define MASK_CPUID_REV 0x000000ff -#define MASK_ICR_PIPN 0x00ff0000 -#define MASK_ICR_IE_1_3 0x00000100 -#define MASK_ICR_IE_1_6 0x00008000 -#define MASK_ICR_CCPN 0x000000ff #define MASK_FCX_FCXS 0x000f0000 #define MASK_FCX_FCXO 0x0000ffff diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 114685cce4..284a749e50 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/registerfields.h" #include "cpu.h" #include "exec/exec-all.h" #include "fpu/softfloat-helpers.h" @@ -152,3 +153,47 @@ void psw_write(CPUTriCoreState *env, uint32_t val) fpu_set_state(env); } + +#define FIELD_GETTER_WITH_FEATURE(NAME, REG, FIELD, FEATURE) \ +uint32_t NAME(CPUTriCoreState *env) \ +{ \ + if (tricore_feature(env, TRICORE_FEATURE_##FEATURE)) { \ + return FIELD_EX32(env->REG, REG, FIELD ## _ ## FEATURE); \ + } \ + return FIELD_EX32(env->REG, REG, FIELD ## _13); \ +} + +#define FIELD_GETTER(NAME, REG, FIELD) \ +uint32_t NAME(CPUTriCoreState *env) \ +{ \ + return FIELD_EX32(env->REG, REG, FIELD); \ +} + +#define FIELD_SETTER_WITH_FEATURE(NAME, REG, FIELD, FEATURE) \ +void NAME(CPUTriCoreState *env, uint32_t val) \ +{ \ + if (tricore_feature(env, TRICORE_FEATURE_##FEATURE)) { \ + env->REG = FIELD_DP32(env->REG, REG, FIELD ## _ ## FEATURE, val); \ + } \ + env->REG = FIELD_DP32(env->REG, REG, FIELD ## _13, val); \ +} + +#define FIELD_SETTER(NAME, REG, FIELD) \ +void NAME(CPUTriCoreState *env, uint32_t val) \ +{ \ + env->REG = FIELD_DP32(env->REG, REG, FIELD, val); \ +} + +FIELD_GETTER_WITH_FEATURE(pcxi_get_pcpn, PCXI, PCPN, 161) +FIELD_SETTER_WITH_FEATURE(pcxi_set_pcpn, PCXI, PCPN, 161) +FIELD_GETTER_WITH_FEATURE(pcxi_get_pie, PCXI, PIE, 161) +FIELD_SETTER_WITH_FEATURE(pcxi_set_pie, PCXI, PIE, 161) +FIELD_GETTER_WITH_FEATURE(pcxi_get_ul, PCXI, UL, 161) +FIELD_SETTER_WITH_FEATURE(pcxi_set_ul, PCXI, UL, 161) +FIELD_GETTER(pcxi_get_pcxs, PCXI, PCXS) +FIELD_GETTER(pcxi_get_pcxo, PCXI, PCXO) + +FIELD_GETTER_WITH_FEATURE(icr_get_ie, ICR, IE, 161) +FIELD_SETTER_WITH_FEATURE(icr_set_ie, ICR, IE, 161) +FIELD_GETTER(icr_get_ccpn, ICR, CCPN) +FIELD_SETTER(icr_set_ccpn, ICR, CCPN) diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index 532ae6b74c..6fd2cbe20f 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -84,11 +84,10 @@ void raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin ICR.IE and ICR.CCPN are saved */ /* PCXI.PIE = ICR.IE */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); + /* PCXI.PCPN = ICR.CCPN */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); /* Update PC using the trap vector table */ env->PC = env->BTV | (class << 5); @@ -2461,13 +2460,11 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) save_context_upper(env, ea); /* PCXI.PCPN = ICR.CCPN; */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); /* PCXI.PIE = ICR.IE; */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); /* PCXI.UL = 1; */ - env->PCXI |= MASK_PCXI_UL; + pcxi_set_ul(env, 1); /* PCXI[19: 0] = FCX[19: 0]; */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); @@ -2506,7 +2503,7 @@ void helper_ret(CPUTriCoreState *env) raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 0) then trap(CTYP); */ - if ((env->PCXI & MASK_PCXI_UL) == 0) { + if (pcxi_get_ul(env) == 0) { /* CTYP trap */ cdc_increment(&psw); /* restore to the start of helper */ psw_write(env, psw); @@ -2516,8 +2513,8 @@ void helper_ret(CPUTriCoreState *env) env->PC = env->gpr_a[11] & 0xfffffffe; /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ - ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + - ((env->PCXI & MASK_PCXI_PCXO) << 6); + ea = (pcxi_get_pcxs(env) << 28) | + (pcxi_get_pcxo(env) << 6); /* {new_PCXI, new_PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ restore_context_upper(env, ea, &new_PCXI, &new_PSW); @@ -2559,21 +2556,21 @@ void helper_bisr(CPUTriCoreState *env, uint32_t const9) /* PCXI.PCPN = ICR.CCPN */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); /* PCXI.PIE = ICR.IE */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); /* PCXI.UL = 0 */ - env->PCXI &= ~(MASK_PCXI_UL); + pcxi_set_ul(env, 0); + /* PCXI[19: 0] = FCX[19: 0] */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); /* FXC[19: 0] = new_FCX[19: 0] */ env->FCX = (env->FCX & 0xfff00000) + (new_FCX & 0xfffff); - /* ICR.IE = 1 */ - env->ICR |= MASK_ICR_IE_1_3; - env->ICR |= const9; /* ICR.CCPN = const9[7: 0];*/ + /* ICR.IE = 1 */ + icr_set_ie(env, 1); + + icr_set_ccpn(env, const9); if (tmp_FCX == env->LCX) { /* FCD trap */ @@ -2592,7 +2589,7 @@ void helper_rfe(CPUTriCoreState *env) raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 0) then trap(CTYP); */ - if ((env->PCXI & MASK_PCXI_UL) == 0) { + if (pcxi_get_ul(env) == 0) { /* raise CTYP trap */ raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } @@ -2603,14 +2600,15 @@ void helper_rfe(CPUTriCoreState *env) } env->PC = env->gpr_a[11] & ~0x1; /* ICR.IE = PCXI.PIE; */ - env->ICR = (env->ICR & ~MASK_ICR_IE_1_3) - + ((env->PCXI & MASK_PCXI_PIE_1_3) >> 15); + icr_set_ie(env, pcxi_get_pie(env)); + /* ICR.CCPN = PCXI.PCPN; */ - env->ICR = (env->ICR & ~MASK_ICR_CCPN) + - ((env->PCXI & MASK_PCXI_PCPN) >> 24); + icr_set_ccpn(env, pcxi_get_pcpn(env)); + /*EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0};*/ - ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + - ((env->PCXI & MASK_PCXI_PCXO) << 6); + ea = (pcxi_get_pcxs(env) << 28) | + (pcxi_get_pcxo(env) << 6); + /*{new_PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ restore_context_upper(env, ea, &new_PCXI, &new_PSW); @@ -2628,11 +2626,10 @@ void helper_rfm(CPUTriCoreState *env) { env->PC = (env->gpr_a[11] & ~0x1); /* ICR.IE = PCXI.PIE; */ - env->ICR = (env->ICR & ~MASK_ICR_IE_1_3) - | ((env->PCXI & MASK_PCXI_PIE_1_3) >> 15); + icr_set_ie(env, pcxi_get_pie(env)); /* ICR.CCPN = PCXI.PCPN; */ - env->ICR = (env->ICR & ~MASK_ICR_CCPN) | - ((env->PCXI & MASK_PCXI_PCPN) >> 24); + icr_set_ccpn(env, pcxi_get_pcpn(env)); + /* {PCXI, PSW, A[10], A[11]} = M(DCX, 4 * word); */ env->PCXI = cpu_ldl_data(env, env->DCX); psw_write(env, cpu_ldl_data(env, env->DCX+4)); @@ -2691,13 +2688,13 @@ void helper_svlcx(CPUTriCoreState *env) save_context_lower(env, ea); /* PCXI.PCPN = ICR.CCPN; */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); + /* PCXI.PIE = ICR.IE; */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); + /* PCXI.UL = 0; */ - env->PCXI &= ~MASK_PCXI_UL; + pcxi_set_ul(env, 0); /* PCXI[19: 0] = FCX[19: 0]; */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); @@ -2734,13 +2731,13 @@ void helper_svucx(CPUTriCoreState *env) save_context_upper(env, ea); /* PCXI.PCPN = ICR.CCPN; */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); + /* PCXI.PIE = ICR.IE; */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); + /* PCXI.UL = 1; */ - env->PCXI |= MASK_PCXI_UL; + pcxi_set_ul(env, 1); /* PCXI[19: 0] = FCX[19: 0]; */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); @@ -2764,13 +2761,15 @@ void helper_rslcx(CPUTriCoreState *env) raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 1) then trap(CTYP); */ - if ((env->PCXI & MASK_PCXI_UL) != 0) { + if (pcxi_get_ul(env) == 1) { /* CTYP trap */ raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ - ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + - ((env->PCXI & MASK_PCXI_PCXO) << 6); + /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ + ea = (pcxi_get_pcxs(env) << 28) | + (pcxi_get_pcxo(env) << 6); + /* {new_PCXI, A[11], A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ restore_context_lower(env, ea, &env->gpr_a[11], &new_PCXI); diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 8e4f99478c..cd33a1dcdd 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -75,6 +75,7 @@ typedef struct DisasContext { int mem_idx; uint32_t hflags, saved_hflags; uint64_t features; + uint32_t icr_ie_mask; } DisasContext; static int has_feature(DisasContext *ctx, int feature) @@ -7850,12 +7851,12 @@ static void decode_sys_interrupts(DisasContext *ctx) /* raise EXCP_DEBUG */ break; case OPC2_32_SYS_DISABLE: - tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~MASK_ICR_IE_1_3); + tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); break; case OPC2_32_SYS_DSYNC: break; case OPC2_32_SYS_ENABLE: - tcg_gen_ori_tl(cpu_ICR, cpu_ICR, MASK_ICR_IE_1_3); + tcg_gen_ori_tl(cpu_ICR, cpu_ICR, ctx->icr_ie_mask); break; case OPC2_32_SYS_ISYNC: break; @@ -8259,6 +8260,11 @@ static void tricore_tr_init_disas_context(DisasContextBase *dcbase, ctx->mem_idx = cpu_mmu_index(env, false); ctx->hflags = (uint32_t)ctx->base.tb->flags; ctx->features = env->features; + if (has_feature(ctx, TRICORE_FEATURE_161)) { + ctx->icr_ie_mask = R_ICR_IE_161_MASK; + } else { + ctx->icr_ie_mask = R_ICR_IE_13_MASK; + } } static void tricore_tr_tb_start(DisasContextBase *db, CPUState *cpu) From 12b95dc432eb9a50569258ffa094a029f3cc13dc Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 26 May 2023 08:19:45 +0200 Subject: [PATCH 213/412] target/tricore: Fix wrong PSW for call insns we were copying PSW into a local variable, updated PSW.CDE in the local and never wrote it back. So when we called save_context_upper() we were using the non-local version of PSW which did not contain the updated PSW.CDE. Signed-off-by: Bastian Koppelmann Message-Id: <20230526061946.54514-6-kbastian@mail.uni-paderborn.de> --- target/tricore/op_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index 6fd2cbe20f..54f54811d9 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -2447,6 +2447,8 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) } /* PSW.CDE = 1;*/ psw |= MASK_PSW_CDE; + psw_write(env, psw); + /* tmp_FCX = FCX; */ tmp_FCX = env->FCX; /* EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0}; */ From e926c94171ae37397c8c4b54cef60e5c7ebbf243 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 26 May 2023 08:19:46 +0200 Subject: [PATCH 214/412] tests/tcg/tricore: Add recursion test for CSAs Signed-off-by: Bastian Koppelmann Message-Id: <20230526061946.54514-7-kbastian@mail.uni-paderborn.de> --- tests/tcg/tricore/Makefile.softmmu-target | 3 ++- tests/tcg/tricore/c/test_context_save_areas.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/tricore/c/test_context_save_areas.c diff --git a/tests/tcg/tricore/Makefile.softmmu-target b/tests/tcg/tricore/Makefile.softmmu-target index f051444991..aff7c1b580 100644 --- a/tests/tcg/tricore/Makefile.softmmu-target +++ b/tests/tcg/tricore/Makefile.softmmu-target @@ -4,7 +4,7 @@ C_TESTS_PATH = $(TESTS_PATH)/c LDFLAGS = -T$(TESTS_PATH)/link.ld --mcpu=tc162 ASFLAGS = -mtc162 -CFLAGS = -mtc162 -c +CFLAGS = -mtc162 -c -I$(TESTS_PATH) TESTS += test_abs.asm.tst TESTS += test_bmerge.asm.tst @@ -23,6 +23,7 @@ TESTS += test_msub.asm.tst TESTS += test_muls.asm.tst TESTS += test_boot_to_main.c.tst +TESTS += test_context_save_areas.c.tst QEMU_OPTS += -M tricore_testboard -cpu tc27x -nographic -kernel diff --git a/tests/tcg/tricore/c/test_context_save_areas.c b/tests/tcg/tricore/c/test_context_save_areas.c new file mode 100644 index 0000000000..a300ee2f9c --- /dev/null +++ b/tests/tcg/tricore/c/test_context_save_areas.c @@ -0,0 +1,15 @@ +#include "testdev_assert.h" + +static int fib(int n) +{ + if (n == 1 || n == 2) { + return 1; + } + return fib(n - 2) + fib(n - 1); +} + +int main(int argc, char **argv) +{ + testdev_assert(fib(10) == 55); + return 0; +} From f6b0de53fb87ddefed348a39284c8e2f28dc4eda Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Wed, 7 Jun 2023 18:29:33 +0200 Subject: [PATCH 215/412] 9pfs: prevent opening special files (CVE-2023-2861) The 9p protocol does not specifically define how server shall behave when client tries to open a special file, however from security POV it does make sense for 9p server to prohibit opening any special file on host side in general. A sane Linux 9p client for instance would never attempt to open a special file on host side, it would always handle those exclusively on its guest side. A malicious client however could potentially escape from the exported 9p tree by creating and opening a device file on host side. With QEMU this could only be exploited in the following unsafe setups: - Running QEMU binary as root AND 9p 'local' fs driver AND 'passthrough' security model. or - Using 9p 'proxy' fs driver (which is running its helper daemon as root). These setups were already discouraged for safety reasons before, however for obvious reasons we are now tightening behaviour on this. Fixes: CVE-2023-2861 Reported-by: Yanwu Shen Reported-by: Jietao Xiao Reported-by: Jinku Li Reported-by: Wenbo Shen Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Reviewed-by: Michael Tokarev Message-Id: --- fsdev/virtfs-proxy-helper.c | 27 +++++++++++++++++++++++-- hw/9pfs/9p-util.h | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 5cafcd7703..d9511f429c 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -26,6 +26,7 @@ #include "qemu/xattr.h" #include "9p-iov-marshal.h" #include "hw/9pfs/9p-proxy.h" +#include "hw/9pfs/9p-util.h" #include "fsdev/9p-iov-marshal.h" #define PROGNAME "virtfs-proxy-helper" @@ -338,6 +339,28 @@ static void resetugid(int suid, int sgid) } } +/* + * Open regular file or directory. Attempts to open any special file are + * rejected. + * + * returns file descriptor or -1 on error + */ +static int open_regular(const char *pathname, int flags, mode_t mode) +{ + int fd; + + fd = open(pathname, flags, mode); + if (fd < 0) { + return fd; + } + + if (close_if_special_file(fd) < 0) { + return -1; + } + + return fd; +} + /* * send response in two parts * 1) ProxyHeader @@ -682,7 +705,7 @@ static int do_create(struct iovec *iovec) if (ret < 0) { goto unmarshal_err_out; } - ret = open(path.data, flags, mode); + ret = open_regular(path.data, flags, mode); if (ret < 0) { ret = -errno; } @@ -707,7 +730,7 @@ static int do_open(struct iovec *iovec) if (ret < 0) { goto err_out; } - ret = open(path.data, flags); + ret = open_regular(path.data, flags, 0); if (ret < 0) { ret = -errno; } diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index c314cf381d..df1b583a5e 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -13,6 +13,8 @@ #ifndef QEMU_9P_UTIL_H #define QEMU_9P_UTIL_H +#include "qemu/error-report.h" + #ifdef O_PATH #define O_PATH_9P_UTIL O_PATH #else @@ -95,6 +97,7 @@ static inline int errno_to_dotl(int err) { #endif #define qemu_openat openat +#define qemu_fstat fstat #define qemu_fstatat fstatat #define qemu_mkdirat mkdirat #define qemu_renameat renameat @@ -108,6 +111,38 @@ static inline void close_preserve_errno(int fd) errno = serrno; } +/** + * close_if_special_file() - Close @fd if neither regular file nor directory. + * + * @fd: file descriptor of open file + * Return: 0 on regular file or directory, -1 otherwise + * + * CVE-2023-2861: Prohibit opening any special file directly on host + * (especially device files), as a compromised client could potentially gain + * access outside exported tree under certain, unsafe setups. We expect + * client to handle I/O on special files exclusively on guest side. + */ +static inline int close_if_special_file(int fd) +{ + struct stat stbuf; + + if (qemu_fstat(fd, &stbuf) < 0) { + close_preserve_errno(fd); + return -1; + } + if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)) { + error_report_once( + "9p: broken or compromised client detected; attempt to open " + "special file (i.e. neither regular file, nor directory)" + ); + close(fd); + errno = ENXIO; + return -1; + } + + return 0; +} + static inline int openat_dir(int dirfd, const char *name) { return qemu_openat(dirfd, name, @@ -142,6 +177,10 @@ again: return -1; } + if (close_if_special_file(fd) < 0) { + return -1; + } + serrno = errno; /* O_NONBLOCK was only needed to open the file. Let's drop it. We don't * do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat() From c45309f7a40083e5034fcb19e27e3c0b1b5ec6cd Mon Sep 17 00:00:00 2001 From: Jagannathan Raman Date: Wed, 7 Jun 2023 11:52:12 -0400 Subject: [PATCH 216/412] maintainers: update maintainers list for vfio-user & multi-process QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jagannathan Raman Reviewed-by: Stefan Hajnoczi Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 436b3f0afe..4a80a38511 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3786,7 +3786,6 @@ F: tests/tcg/aarch64/system/semiheap.c Multi-process QEMU M: Elena Ufimtseva M: Jagannathan Raman -M: John G Johnson S: Maintained F: docs/devel/multi-process.rst F: docs/system/multi-process.rst From 3673ad389622d9ef4d2743101253c642def7935a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 8 Jun 2023 09:29:25 -0700 Subject: [PATCH 217/412] tcg/tci: Fix MemOpIdx operand index for 3-operand memops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cut and paste error from the 4-operand memops. Fixes: ab64da797740 ("tcg/tci: Adjust passing of MemOpIdx") Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230608162925.677598-1-richard.henderson@linaro.org> --- tcg/tci/tcg-target.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0037f904f1..253f27f174 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -847,7 +847,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, if (TCG_TARGET_REG_BITS == 64) { tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); } else { - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[4]); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); } break; From 9c36407a4445de1f3e69d7a7a30e86dbb75a94e6 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Wed, 31 May 2023 14:48:31 +0200 Subject: [PATCH 218/412] docs: Fix trivial typos in vhost-user.rst Signed-off-by: Milan Zamazal Signed-off-by: Michael Tokarev --- docs/system/devices/vhost-user.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/system/devices/vhost-user.rst b/docs/system/devices/vhost-user.rst index 86128114fa..a80e95a48a 100644 --- a/docs/system/devices/vhost-user.rst +++ b/docs/system/devices/vhost-user.rst @@ -38,13 +38,13 @@ system memory as defined by the ``-m`` argument. Example ======= -First start you daemon. +First start your daemon. .. parsed-literal:: $ virtio-foo --socket-path=/var/run/foo.sock $OTHER_ARGS -The you start your QEMU instance specifying the device, chardev and +Then you start your QEMU instance specifying the device, chardev and memory objects. .. parsed-literal:: From 46e75a77a9f5a5d88e4c8bb6de37f1531aa7d8b0 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 1 Apr 2023 11:51:40 +0300 Subject: [PATCH 219/412] hw/virtio/virtio-qmp.c: spelling: suppoted Fixes: f3034ad71fcd0a6a58bc37830f182b307f089159 Signed-off-by: Michael Tokarev Reviewed-by: Stefan Weil --- hw/virtio/virtio-qmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index b5e1835299..3528fc628d 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -331,7 +331,7 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = { static const qmp_virtio_feature_map_t virtio_scsi_feature_map[] = { FEATURE_ENTRY(VIRTIO_SCSI_F_INOUT, \ "VIRTIO_SCSI_F_INOUT: Requests including read and writable data " - "buffers suppoted"), + "buffers supported"), FEATURE_ENTRY(VIRTIO_SCSI_F_HOTPLUG, \ "VIRTIO_SCSI_F_HOTPLUG: Reporting and handling hot-plug events " "supported"), From 40b89515d026915a0593993712e322addac095b9 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Thu, 20 Apr 2023 22:55:41 +0300 Subject: [PATCH 220/412] spelling: information 3 trivial fixes: 2 .json comments which goes to executables, and 1 .h file comment. Signed-off-by: Michael Tokarev --- include/ui/clipboard.h | 2 +- qapi/cryptodev.json | 2 +- qga/qapi-schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index ce76aa451f..ab6acdbd8a 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -170,7 +170,7 @@ void qemu_clipboard_peer_release(QemuClipboardPeer *peer, * * @selection: clipboard selection. * - * Return the current clipboard data & owner informations. + * Return the current clipboard data & owner information. */ QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection); diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json index 77f48a9c21..68289f4984 100644 --- a/qapi/cryptodev.json +++ b/qapi/cryptodev.json @@ -75,7 +75,7 @@ # # @service: supported service types of a crypto device # -# @client: the additional infomation of the crypto device +# @client: the additional information of the crypto device # # Since: 8.0 ## diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 42fb046eb7..b720dd4379 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -931,7 +931,7 @@ ## # @GuestNVMeSmart: # -# NVMe smart informations, based on NVMe specification, section +# NVMe smart information, based on NVMe specification, section # # # Since: 7.1 From 5fb9e8295531f957cf7ac20e89736c8963a25e04 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Wed, 26 Apr 2023 09:35:18 +0000 Subject: [PATCH 221/412] hw/remote: Fix vfu_cfg trace offset format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The printed offset value is prefixed with 0x, but was actually printed in decimal. To spare others the confusion, adjust the format specifier to hexadecimal. Signed-off-by: Mattias Nissler Reviewed-by: Jagannathan Raman Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Michael Tokarev --- hw/remote/trace-events | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/remote/trace-events b/hw/remote/trace-events index c167b3c7a5..0d1b7d56a5 100644 --- a/hw/remote/trace-events +++ b/hw/remote/trace-events @@ -5,8 +5,8 @@ mpqemu_recv_io_error(int cmd, int size, int nfds) "failed to receive %d size %d, # vfio-user-obj.c vfu_prop(const char *prop, const char *val) "vfu: setting %s as %s" -vfu_cfg_read(uint32_t offset, uint32_t val) "vfu: cfg: 0x%u -> 0x%x" -vfu_cfg_write(uint32_t offset, uint32_t val) "vfu: cfg: 0x%u <- 0x%x" +vfu_cfg_read(uint32_t offset, uint32_t val) "vfu: cfg: 0x%x -> 0x%x" +vfu_cfg_write(uint32_t offset, uint32_t val) "vfu: cfg: 0x%x <- 0x%x" vfu_dma_register(uint64_t gpa, size_t len) "vfu: registering GPA 0x%"PRIx64", %zu bytes" vfu_dma_unregister(uint64_t gpa) "vfu: unregistering GPA 0x%"PRIx64"" vfu_bar_register(int i, uint64_t addr, uint64_t size) "vfu: BAR %d: addr 0x%"PRIx64" size 0x%"PRIx64"" From fbdffb08dfe5db0ffed22619bad1772a675e11e2 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Wed, 5 Apr 2023 16:34:04 +0300 Subject: [PATCH 222/412] block.c: add newline for "Detected format" warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the forgotten trailing newline. Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé --- block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block.c b/block.c index dae629075c..0637265c26 100644 --- a/block.c +++ b/block.c @@ -7158,7 +7158,7 @@ void bdrv_img_create(const char *filename, const char *fmt, if (!backing_fmt) { error_setg(&local_err, "Backing file specified without backing format"); - error_append_hint(&local_err, "Detected format of %s.", + error_append_hint(&local_err, "Detected format of %s.\n", bs->drv->format_name); goto out; } From 890e37e27cd6b38a6e9bccf48c05700bab429b53 Mon Sep 17 00:00:00 2001 From: Carlos Santos Date: Mon, 27 Mar 2023 14:21:47 -0300 Subject: [PATCH 223/412] meson: install keyboard maps only if necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are required only for system emulation (i.e. have_system is true). Signed-off-by: Carlos Santos Reviewed-by: Daniel P. Berrangé Signed-off-by: Michael Tokarev --- pc-bios/keymaps/meson.build | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pc-bios/keymaps/meson.build b/pc-bios/keymaps/meson.build index 158a3b410c..bff3083313 100644 --- a/pc-bios/keymaps/meson.build +++ b/pc-bios/keymaps/meson.build @@ -47,7 +47,7 @@ if native_qemu_keymap.found() build_by_default: true, output: km, command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], - install: true, + install: have_system, install_dir: qemu_datadir / 'keymaps') endforeach @@ -56,4 +56,6 @@ else install_data(keymaps.keys(), install_dir: qemu_datadir / 'keymaps') endif -install_data(['sl', 'sv'], install_dir: qemu_datadir / 'keymaps') +if have_system + install_data(['sl', 'sv'], install_dir: qemu_datadir / 'keymaps') +endif From d8ca9712f58f05f5668c43678f132330f2636ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 21 Mar 2023 10:49:50 +0100 Subject: [PATCH 224/412] target/m68k/fpu_helper: Use FloatRelation enum to hold comparison result MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the FloatRelation enum to hold the comparison result (missed in commit 71bfd65c5f "softfloat: Name compare relation enum"). Inspired-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Michael Tokarev --- target/m68k/fpu_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 3a37d8f584..ab120b5f59 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -349,7 +349,7 @@ void HELPER(fsgldiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) PREC_END(); } -static int float_comp_to_cc(int float_compare) +static int float_comp_to_cc(FloatRelation float_compare) { switch (float_compare) { case float_relation_equal: @@ -367,7 +367,7 @@ static int float_comp_to_cc(int float_compare) void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1) { - int float_compare; + FloatRelation float_compare; float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status); env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare); From bec552e2cdb1950e2cd5f1853c396ea91ec80253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 23 May 2023 08:12:04 +0200 Subject: [PATCH 225/412] hw/core/cpu: Simplify realize() using MACHINE_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Michael Tokarev --- hw/core/cpu-common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index f4e51c8a1b..ced66c2b34 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -196,8 +196,7 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) * no need to check the ignore_memory_transaction_failures board flag. */ if (object_dynamic_cast(machine, TYPE_MACHINE)) { - ObjectClass *oc = object_get_class(machine); - MachineClass *mc = MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_GET_CLASS(machine); if (mc) { cpu->ignore_memory_transaction_failures = From a5c80ab847dada26137461e534f75bb9bcb85a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 23 May 2023 08:12:05 +0200 Subject: [PATCH 226/412] hw/i386/microvm: Simplify using object_dynamic_cast() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use object_dynamic_cast() to determine if 'dev' is a TYPE_VIRTIO_MMIO. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Sergio Lopez Signed-off-by: Michael Tokarev --- hw/i386/microvm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 3d606a20b4..7227a2156c 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -389,9 +389,8 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) bus = sysbus_get_default(); QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; - ObjectClass *class = object_get_class(OBJECT(dev)); - if (class == object_class_by_name(TYPE_VIRTIO_MMIO)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO)) { VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev)); VirtioBusState *mmio_virtio_bus = &mmio->bus; BusState *mmio_bus = &mmio_virtio_bus->parent_obj; From 271233f21f66c10194a45c1bff1db61fe2694a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 23 May 2023 08:12:06 +0200 Subject: [PATCH 227/412] hw/pci/pci: Simplify pci_bar_address() using MACHINE_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary intermediate variables. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Michael Tokarev --- hw/pci/pci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 9b7b4d7c18..bf38905b7d 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1446,9 +1446,7 @@ pcibus_t pci_bar_address(PCIDevice *d, { pcibus_t new_addr, last_addr; uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); - Object *machine = qdev_get_machine(); - ObjectClass *oc = object_get_class(machine); - MachineClass *mc = MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); bool allow_0_address = mc->pci_allow_0_address; if (type & PCI_BASE_ADDRESS_SPACE_IO) { From 4c030dd00f617b432524f7f9627192cb9b328bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 23 May 2023 08:12:07 +0200 Subject: [PATCH 228/412] hw/usb/hcd-ehci-pci: Simplify using DEVICE_GET_CLASS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Michael Tokarev --- hw/usb/hcd-ehci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 4c37c8e227..345444a573 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -74,7 +74,7 @@ static void usb_ehci_pci_realize(PCIDevice *dev, Error **errp) static void usb_ehci_pci_init(Object *obj) { - DeviceClass *dc = OBJECT_GET_CLASS(DeviceClass, obj, TYPE_DEVICE); + DeviceClass *dc = DEVICE_GET_CLASS(obj); EHCIPCIState *i = PCI_EHCI(obj); EHCIState *s = &i->ehci; From 725160fe56eb9f6b9b13214b9adf519c25b9d527 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 3 Jun 2023 20:23:38 +0300 Subject: [PATCH 229/412] linux-user: add comments for TARGET_NR_[gs]etgroups{,32} There are 2 pairs of identical code (with different types) for TARGET_NR_setgroups & TARGET_NR_setgroups32, and for TARGET_NR_getgroups & TARGET_NR_getgroups32. Add comments stating this fact, so that further modifications are done in two places. Signed-off-by: Michael Tokarev --- linux-user/syscall.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 83685f0aa5..94256cc262 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -11670,7 +11670,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_setregid: return get_errno(setregid(low2highgid(arg1), low2highgid(arg2))); case TARGET_NR_getgroups: - { + { /* the same code as for TARGET_NR_getgroups32 */ int gidsetsize = arg1; target_id *target_grouplist; g_autofree gid_t *grouplist = NULL; @@ -11701,7 +11701,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return ret; } case TARGET_NR_setgroups: - { + { /* the same code as for TARGET_NR_setgroups32 */ int gidsetsize = arg1; target_id *target_grouplist; g_autofree gid_t *grouplist = NULL; @@ -12006,7 +12006,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_getgroups32 case TARGET_NR_getgroups32: - { + { /* the same code as for TARGET_NR_getgroups */ int gidsetsize = arg1; uint32_t *target_grouplist; g_autofree gid_t *grouplist = NULL; @@ -12038,7 +12038,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setgroups32 case TARGET_NR_setgroups32: - { + { /* the same code as for TARGET_NR_setgroups */ int gidsetsize = arg1; uint32_t *target_grouplist; g_autofree gid_t *grouplist = NULL; From 8fbf89a9669520ac09b3ae0013ff3eb34f8cab23 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 9 Jun 2023 17:29:15 +0100 Subject: [PATCH 230/412] linux-user: Return EINVAL for getgroups() with negative gidsetsize Coverity doesn't like the way we might end up calling getgroups() with a NULL grouplist pointer. This is fine for the special case of gidsetsize == 0, but we will also do it if the guest passes us a negative gidsetsize. (CID 1512465) Explicitly fail the negative gidsetsize with EINVAL, as the kernel does. This means we definitely only call the libc getgroups() with valid parameters. It also brings the getgroups() code in to line with the setgroups() code. Possibly Coverity may still complain about getgroups(0, NULL), but that would be a false positive. Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- linux-user/syscall.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 94256cc262..f2cb101d83 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -11676,7 +11676,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, g_autofree gid_t *grouplist = NULL; int i; - if (gidsetsize > NGROUPS_MAX) { + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { return -TARGET_EINVAL; } if (gidsetsize > 0) { @@ -12012,7 +12012,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, g_autofree gid_t *grouplist = NULL; int i; - if (gidsetsize > NGROUPS_MAX) { + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { return -TARGET_EINVAL; } if (gidsetsize > 0) { From bdfca8a22f41e7ad47fd2dac71e4d1387e2c0d4e Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Fri, 9 Jun 2023 12:23:06 +0300 Subject: [PATCH 231/412] vnc: move assert in vnc_worker_thread_loop job may be NULL if queue->exit is true. Check it before dereference job. Fixes: f31f9c1080 ("vnc: add magic cookie to VncState") Signed-off-by: Anastasia Belova Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- ui/vnc-jobs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 886f9bf611..fcca7ec632 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -250,12 +250,13 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Here job can only be NULL if queue->exit is true */ job = QTAILQ_FIRST(&queue->jobs); vnc_unlock_queue(queue); - assert(job->vs->magic == VNC_MAGIC); if (queue->exit) { return -1; } + assert(job->vs->magic == VNC_MAGIC); + vnc_lock_output(job->vs); if (job->vs->ioc == NULL || job->vs->abort == true) { vnc_unlock_output(job->vs); From f101c25cd66dc4dea1135d2f1783a0786534923a Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 27 Mar 2023 22:25:23 +1030 Subject: [PATCH 232/412] linux-user: elfload: s/min_mmap_addr/mmap_min_addr/ As-is the error message can cause some confusion as the mentioned sysctl attribute name is wrong: https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#mmap-min-addr Signed-off-by: Andrew Jeffery Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- linux-user/elfload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d80d68484b..76874833e3 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2798,7 +2798,7 @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr, if (addr == MAP_FAILED || addr != test) { error_report("Unable to reserve 0x%lx bytes of virtual address " "space at %p (%s) for use as guest address space (check your " - "virtual memory ulimit setting, min_mmap_addr or reserve less " + "virtual memory ulimit setting, mmap_min_addr or reserve less " "using -R option)", reserved_va + 1, test, strerror(errno)); exit(EXIT_FAILURE); } From e928907105cfeb48b68cedce232bbd4784536707 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 27 Mar 2023 22:25:24 +1030 Subject: [PATCH 233/412] linux-user: elfload: Specify -R is an option for qemu-user binaries Given several different concepts are suggested for investigation, let's not confuse e.g. ulimit's -R with what was actually intended. Signed-off-by: Andrew Jeffery Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- linux-user/elfload.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 76874833e3..9a2ec568b0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2799,7 +2799,8 @@ static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr, error_report("Unable to reserve 0x%lx bytes of virtual address " "space at %p (%s) for use as guest address space (check your " "virtual memory ulimit setting, mmap_min_addr or reserve less " - "using -R option)", reserved_va + 1, test, strerror(errno)); + "using qemu-user's -R option)", + reserved_va + 1, test, strerror(errno)); exit(EXIT_FAILURE); } From cce84fc9193e2566b4f7f9de5a13e7ed360d44d9 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Thu, 1 Jun 2023 14:13:27 +0200 Subject: [PATCH 234/412] pnv/xive2: Add definition for TCTXT Config register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add basic read/write support for the TCTXT Config register on P10. qemu doesn't do anything with it yet, but it avoids logging a guest error when skiboot configures the fused-core state: qemu-system-ppc64 -machine powernv10 ... -d guest_errors ... [ 0.131670000,5] XIVE: [ IC 00 ] Initializing XIVE block ID 0... XIVE[0] - TCTXT: invalid read @140 XIVE[0] - TCTXT: invalid write @140 Signed-off-by: Frederic Barrat Reviewed-by: Cédric Le Goater Message-Id: <20230601121331.487207-2-fbarrat@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/intc/pnv_xive2.c | 8 +++++++- hw/intc/pnv_xive2_regs.h | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 7176d70234..889e409929 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1265,6 +1265,9 @@ static uint64_t pnv_xive2_ic_tctxt_read(void *opaque, hwaddr offset, case TCTXT_EN1_RESET: val = xive->tctxt_regs[TCTXT_EN1 >> 3]; break; + case TCTXT_CFG: + val = xive->tctxt_regs[reg]; + break; default: xive2_error(xive, "TCTXT: invalid read @%"HWADDR_PRIx, offset); } @@ -1276,6 +1279,7 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t reg = offset >> 3; switch (offset) { /* @@ -1297,7 +1301,9 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, case TCTXT_EN1_RESET: xive->tctxt_regs[TCTXT_EN1 >> 3] &= ~val; break; - + case TCTXT_CFG: + xive->tctxt_regs[reg] = val; + break; default: xive2_error(xive, "TCTXT: invalid write @%"HWADDR_PRIx, offset); return; diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index 0c096e4adb..8f1e0a1fde 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -405,6 +405,10 @@ #define X_TCTXT_EN1_RESET 0x307 #define TCTXT_EN1_RESET 0x038 +/* TCTXT Config register */ +#define X_TCTXT_CFG 0x328 +#define TCTXT_CFG 0x140 + /* * VSD Tables */ From 32af01f83a763ccbba39c1cbc424e1b724d233df Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Thu, 1 Jun 2023 14:13:28 +0200 Subject: [PATCH 235/412] pnv/xive2: Add definition for the ESB cache configuration register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add basic read/write support for the ESB cache configuration register on P10. We don't model the ESB cache in qemu so reading/writing the register won't do anything, but it avoids logging a guest error when skiboot configures it: qemu-system-ppc64 -machine powernv10 ... -d guest_errors ... XIVE[0] - VC: invalid read @240 XIVE[0] - VC: invalid write @240 Signed-off-by: Frederic Barrat Reviewed-by: Cédric Le Goater Message-Id: <20230601121331.487207-3-fbarrat@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/intc/pnv_xive2.c | 7 +++++++ hw/intc/pnv_xive2_regs.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 889e409929..a75ff270ac 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -955,6 +955,10 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, val = xive->vc_regs[reg]; break; + case VC_ESBC_CFG: + val = xive->vc_regs[reg]; + break; + /* * EAS cache updates (not modeled) */ @@ -1046,6 +1050,9 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* ESB update */ break; + case VC_ESBC_CFG: + break; + /* * EAS cache updates (not modeled) */ diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index 8f1e0a1fde..7165dc8704 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -232,6 +232,10 @@ #define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* ESBC configuration */ +#define X_VC_ESBC_CFG 0x148 +#define VC_ESBC_CFG 0x240 + /* EASC flush control register */ #define X_VC_EASC_FLUSH_CTRL 0x160 #define VC_EASC_FLUSH_CTRL 0x300 From f0fc1c29a8163ce383d3bcb3aac0964747d2d8b1 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Thu, 1 Jun 2023 14:13:29 +0200 Subject: [PATCH 236/412] pnv/xive2: Allow writes to the Physical Thread Enable registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix what was probably a silly mistake and allow to write the Physical Thread enable registers 0 and 1. Skiboot prefers to use the ENx_SET variant so it went unnoticed, but there's no reason to discard a write to the full register, it is Read-Write. Fixes: da71b7e3ed45 ("ppc/pnv: Add a XIVE2 controller to the POWER10 chip") Signed-off-by: Frederic Barrat Reviewed-by: Cédric Le Goater Message-Id: <20230601121331.487207-4-fbarrat@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/intc/pnv_xive2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index a75ff270ac..132f82a035 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1294,6 +1294,7 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, */ case TCTXT_EN0: /* Physical Thread Enable */ case TCTXT_EN1: /* Physical Thread Enable (fused core) */ + xive->tctxt_regs[reg] = val; break; case TCTXT_EN0_SET: From afca92071fc12402a8dee1ad68f66f22dd4b9872 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Thu, 1 Jun 2023 14:13:30 +0200 Subject: [PATCH 237/412] pnv/xive2: Introduce macros to manipulate TIMA addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TIMA addresses are somewhat special and are split in several bit fields with different meanings. This patch describes it and introduce macros to more easily access the various fields. Signed-off-by: Frederic Barrat Reviewed-by: Cédric Le Goater Message-Id: <20230601121331.487207-5-fbarrat@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/intc/xive.c | 14 +++++++------- include/hw/ppc/xive_regs.h | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index a986b96843..ebe399bc09 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -249,7 +249,7 @@ static const uint8_t *xive_tm_views[] = { static uint64_t xive_tm_mask(hwaddr offset, unsigned size, bool write) { uint8_t page_offset = (offset >> TM_SHIFT) & 0x3; - uint8_t reg_offset = offset & 0x3F; + uint8_t reg_offset = offset & TM_REG_OFFSET; uint8_t reg_mask = write ? 0x1 : 0x2; uint64_t mask = 0x0; int i; @@ -266,8 +266,8 @@ static uint64_t xive_tm_mask(hwaddr offset, unsigned size, bool write) static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - uint8_t ring_offset = offset & 0x30; - uint8_t reg_offset = offset & 0x3F; + uint8_t ring_offset = offset & TM_RING_OFFSET; + uint8_t reg_offset = offset & TM_REG_OFFSET; uint64_t mask = xive_tm_mask(offset, size, true); int i; @@ -296,8 +296,8 @@ static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size) { - uint8_t ring_offset = offset & 0x30; - uint8_t reg_offset = offset & 0x3F; + uint8_t ring_offset = offset & TM_RING_OFFSET; + uint8_t reg_offset = offset & TM_REG_OFFSET; uint64_t mask = xive_tm_mask(offset, size, false); uint64_t ret; int i; @@ -534,7 +534,7 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * First, check for special operations in the 2K region */ - if (offset & 0x800) { + if (offset & TM_SPECIAL_OP) { xto = xive_tm_find_op(offset, size, true); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " @@ -573,7 +573,7 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * First, check for special operations in the 2K region */ - if (offset & 0x800) { + if (offset & TM_SPECIAL_OP) { xto = xive_tm_find_op(offset, size, false); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index b7fde2354e..4a3c9badd3 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -48,6 +48,22 @@ #define TM_SHIFT 16 +/* + * TIMA addresses are 12-bits (4k page). + * The MSB indicates a special op with side effect, which can be + * refined with bit 10 (see below). + * The registers, logically grouped in 4 rings (a quad-word each), are + * defined on the 6 LSBs (offset below 0x40) + * In between, we can add a cache line index from 0...3 (ie, 0, 0x80, + * 0x100, 0x180) to select a specific snooper. Those 'snoop port + * address' bits should be dropped when processing the operations as + * they are all equivalent. + */ +#define TM_ADDRESS_MASK 0xC3F +#define TM_SPECIAL_OP 0x800 +#define TM_RING_OFFSET 0x30 +#define TM_REG_OFFSET 0x3F + /* TM register offsets */ #define TM_QW0_USER 0x000 /* All rings */ #define TM_QW1_OS 0x010 /* Ring 0..2 */ From 6f2cbd133d44547fe71879271adaadf11f20965b Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Thu, 1 Jun 2023 14:13:31 +0200 Subject: [PATCH 238/412] pnv/xive2: Handle TIMA access through all ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Thread Interrupt Management Area (TIMA) can be accessed through 4 ports, targeted by the address. The base address of a TIMA is using port 0 and the other ports are 0x80 apart. Using one port or another can be useful to balance the load on the snoop buses. With skiboot and linux, we currently use port 0, but as it tends to be busy, another hypervisor is using port 1 for TIMA access. The port address bits fall in between the special op indication bits (the 2 MSBs) and the register offset bits (the 6 LSBs). They are "don't care" for the hardware when processing a TIMA operation. This patch filters out those port address bits so that a TIMA operation can be triggered using any port. It is also true for indirect access (through the IC BAR) and it's actually nothing new, it was already the case on P9. Which helps here, as the TIMA handling code is common between P9 (xive) and P10 (xive2). Signed-off-by: Frederic Barrat Reviewed-by: Cédric Le Goater Message-Id: <20230601121331.487207-6-fbarrat@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/intc/pnv_xive2.c | 4 ++++ hw/intc/xive.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 132f82a035..e5a028c1e6 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1662,6 +1662,8 @@ static void pnv_xive2_tm_write(void *opaque, hwaddr offset, bool gen1_tima_os = xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + offset &= TM_ADDRESS_MASK; + /* TODO: should we switch the TM ops table instead ? */ if (!gen1_tima_os && offset == HV_PUSH_OS_CTX_OFFSET) { xive2_tm_push_os_ctx(xptr, tctx, offset, value, size); @@ -1681,6 +1683,8 @@ static uint64_t pnv_xive2_tm_read(void *opaque, hwaddr offset, unsigned size) bool gen1_tima_os = xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + offset &= TM_ADDRESS_MASK; + /* TODO: should we switch the TM ops table instead ? */ if (!gen1_tima_os && offset == HV_PULL_OS_CTX_OFFSET) { return xive2_tm_pull_os_ctx(xptr, tctx, offset, size); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index ebe399bc09..5204c14b87 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -500,7 +500,7 @@ static const XiveTmOp xive_tm_operations[] = { static const XiveTmOp *xive_tm_find_op(hwaddr offset, unsigned size, bool write) { uint8_t page_offset = (offset >> TM_SHIFT) & 0x3; - uint32_t op_offset = offset & 0xFFF; + uint32_t op_offset = offset & TM_ADDRESS_MASK; int i; for (i = 0; i < ARRAY_SIZE(xive_tm_operations); i++) { From 6c242e79b876b3570b8fd2f10f2a502467758e56 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:21:27 +1000 Subject: [PATCH 239/412] target/ppc: Fix nested-hv HEAI delivery ppc hypervisors turn HEAI interrupts into program interrupts injected into the guest that executed the illegal instruction, if the hypervisor doesn't handle it some other way. The nested-hv implementation failed to account for this HEAI->program conversion. The virtual hypervisor wants to see the HEAI when running a nested guest, so that interrupt type can be returned to its KVM caller. Fixes: 7cebc5db2eba6 ("target/ppc: Introduce a vhyp framework for nested HV support") Cc: balaton@eik.bme.hu Reviewed-by: Fabiano Rosas Signed-off-by: Nicholas Piggin Message-Id: <20230530132127.385001-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/excp_helper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index fea9221501..9ffcfe788a 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1358,9 +1358,12 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) /* * We don't want to generate a Hypervisor Emulation Assistance - * Interrupt if we don't have HVB in msr_mask (PAPR mode). + * Interrupt if we don't have HVB in msr_mask (PAPR mode), + * unless running a nested-hv guest, in which case the L1 + * kernel wants the interrupt. */ - if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB)) { + if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB) && + !books_vhyp_handles_hv_excp(cpu)) { excp = POWERPC_EXCP_PROGRAM; } From 34b4313070b5f5613fb8198806f57614826b0aac Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Wed, 31 May 2023 17:05:37 +0200 Subject: [PATCH 240/412] pnv/xive2: Quiet down some error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When dumping the END and NVP tables ("info pic" from the HMP) on the P10 model, we're likely to be flooded with error messages such as: XIVE[0] - VST: invalid NVPT entry f33800 !? The error is printed when finding an empty VSD in an indirect table (thus END and NVP tables with skiboot), which is going to happen when dumping the xive state. So let's tune down those messages. They can be re-enabled easily with a macro if needed. Those errors were already hidden on xive/P9, for the same reason. Signed-off-by: Frederic Barrat Reviewed-by: Cédric Le Goater Message-Id: <20230531150537.369350-1-fbarrat@linux.ibm.com> Signed-off-by: Daniel Henrique Barboza --- hw/intc/pnv_xive2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index e5a028c1e6..ec1edeb385 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -163,7 +163,9 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED); if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE2_DEBUG xive2_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif return 0; } @@ -185,7 +187,9 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, MEMTXATTRS_UNSPECIFIED); if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE2_DEBUG xive2_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif return 0; } From 6494d2c1fd4ebc37b575130399a97a1fcfff1afc Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:04:47 +1000 Subject: [PATCH 241/412] target/ppc: Fix PMU hflags calculation Some of the PMU hflags bits can go out of synch, for example a store to MMCR0 with PMCjCE=1 fails to update hflags correctly and results in hflags mismatch: qemu: fatal: TCG hflags mismatch (current:0x2408003d rebuilt:0x240a003d) This can be reproduced by running perf on a recent machine. Some of the fragility here is the duplication of PMU hflags calculations. This change consolidates that in a single place to update pmu-related hflags, to be called after a well defined state changes. The post-load PMU update is pulled out of the MSR update because it does not depend on the MSR value. Fixes: 8b3d1c49a9f0 ("target/ppc: Add new PMC HFLAGS") Signed-off-by: Nicholas Piggin Reviewed-by: Daniel Henrique Barboza Message-Id: <20230530130447.372617-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/cpu_init.c | 2 +- target/ppc/helper_regs.c | 73 ++++++++++++++++++++++++++++++---------- target/ppc/helper_regs.h | 1 + target/ppc/machine.c | 8 ++--- target/ppc/power8-pmu.c | 38 ++++++++++++--------- target/ppc/power8-pmu.h | 4 +-- 6 files changed, 85 insertions(+), 41 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 05bf73296b..398f2d9966 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7083,7 +7083,7 @@ static void ppc_cpu_reset_hold(Object *obj) if (env->mmu_model != POWERPC_MMU_REAL) { ppc_tlb_invalidate_all(env); } - pmu_update_summaries(env); + pmu_mmcr01_updated(env); } /* clean any pending stop state */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index fb351c303f..bc7e9d7eda 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -47,6 +47,48 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) env->tgpr[3] = tmp; } +static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) +{ + uint32_t hflags = 0; + +#if defined(TARGET_PPC64) + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { + hflags |= 1 << HFLAGS_PMCC0; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + hflags |= 1 << HFLAGS_PMCC1; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { + hflags |= 1 << HFLAGS_PMCJCE; + } + +#ifndef CONFIG_USER_ONLY + if (env->pmc_ins_cnt) { + hflags |= 1 << HFLAGS_INSN_CNT; + } + if (env->pmc_ins_cnt & 0x1e) { + hflags |= 1 << HFLAGS_PMC_OTHER; + } +#endif +#endif + + return hflags; +} + +/* Mask of all PMU hflags */ +static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) +{ + uint32_t hflags_mask = 0; +#if defined(TARGET_PPC64) + hflags_mask |= 1 << HFLAGS_PMCC0; + hflags_mask |= 1 << HFLAGS_PMCC1; + hflags_mask |= 1 << HFLAGS_PMCJCE; + hflags_mask |= 1 << HFLAGS_INSN_CNT; + hflags_mask |= 1 << HFLAGS_PMC_OTHER; +#endif + return hflags_mask; +} + static uint32_t hreg_compute_hflags_value(CPUPPCState *env) { target_ulong msr = env->msr; @@ -104,30 +146,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { - hflags |= 1 << HFLAGS_PMCC0; - } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { - hflags |= 1 << HFLAGS_PMCC1; - } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { - hflags |= 1 << HFLAGS_PMCJCE; - } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { hflags |= 1 << HFLAGS_HV; } -#if defined(TARGET_PPC64) - if (env->pmc_ins_cnt) { - hflags |= 1 << HFLAGS_INSN_CNT; - } - if (env->pmc_ins_cnt & 0x1e) { - hflags |= 1 << HFLAGS_PMC_OTHER; - } -#endif - /* * This is our encoding for server processors. The architecture * specifies that there is no such thing as userspace with @@ -172,6 +196,8 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) hflags |= dmmu_idx << HFLAGS_DMMU_IDX; #endif + hflags |= hreg_compute_pmu_hflags_value(env); + return hflags | (msr & msr_mask); } @@ -180,6 +206,17 @@ void hreg_compute_hflags(CPUPPCState *env) env->hflags = hreg_compute_hflags_value(env); } +/* + * This can be used as a lighter-weight alternative to hreg_compute_hflags + * when PMU MMCR0 or pmc_ins_cnt changes. pmc_ins_cnt is changed by + * pmu_update_summaries. + */ +void hreg_update_pmu_hflags(CPUPPCState *env) +{ + env->hflags &= ~hreg_compute_pmu_hflags_mask(env); + env->hflags |= hreg_compute_pmu_hflags_value(env); +} + #ifdef CONFIG_DEBUG_TCG void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 42f26870b9..8196c1346d 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -22,6 +22,7 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env); void hreg_compute_hflags(CPUPPCState *env); +void hreg_update_pmu_hflags(CPUPPCState *env); void cpu_interrupt_exittb(CPUState *cs); int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv); diff --git a/target/ppc/machine.c b/target/ppc/machine.c index be6eb3d968..134b16c625 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -21,10 +21,6 @@ static void post_load_update_msr(CPUPPCState *env) */ env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB); ppc_store_msr(env, msr); - - if (tcg_enabled()) { - pmu_update_summaries(env); - } } static int get_avr(QEMUFile *f, void *pv, size_t size, @@ -317,6 +313,10 @@ static int cpu_post_load(void *opaque, int version_id) post_load_update_msr(env); + if (tcg_enabled()) { + pmu_mmcr01_updated(env); + } + return 0; } diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index 64a64865d7..c4c331c6b5 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -31,7 +31,11 @@ static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn) return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE; } -void pmu_update_summaries(CPUPPCState *env) +/* + * Called after MMCR0 or MMCR1 changes to update pmc_ins_cnt and pmc_cyc_cnt. + * hflags must subsequently be updated. + */ +static void pmu_update_summaries(CPUPPCState *env) { target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0]; target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1]; @@ -39,7 +43,7 @@ void pmu_update_summaries(CPUPPCState *env) int cyc_cnt = 0; if (mmcr0 & MMCR0_FC) { - goto hflags_calc; + goto out; } if (!(mmcr0 & MMCR0_FC14) && mmcr1 != 0) { @@ -73,10 +77,19 @@ void pmu_update_summaries(CPUPPCState *env) ins_cnt |= !(mmcr0 & MMCR0_FC56) << 5; cyc_cnt |= !(mmcr0 & MMCR0_FC56) << 6; - hflags_calc: + out: env->pmc_ins_cnt = ins_cnt; env->pmc_cyc_cnt = cyc_cnt; - env->hflags = deposit32(env->hflags, HFLAGS_INSN_CNT, 1, ins_cnt != 0); +} + +void pmu_mmcr01_updated(CPUPPCState *env) +{ + pmu_update_summaries(env); + hreg_update_pmu_hflags(env); + /* + * Should this update overflow timers (if mmcr0 is updated) so they + * get set in cpu_post_load? + */ } static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns) @@ -234,18 +247,11 @@ static void pmu_delete_timers(CPUPPCState *env) void helper_store_mmcr0(CPUPPCState *env, target_ulong value) { - bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0; - bool hflags_pmcc1 = (value & MMCR0_PMCC1) != 0; - pmu_update_cycles(env); env->spr[SPR_POWER_MMCR0] = value; - /* MMCR0 writes can change HFLAGS_PMCC[01] and HFLAGS_INSN_CNT */ - env->hflags = deposit32(env->hflags, HFLAGS_PMCC0, 1, hflags_pmcc0); - env->hflags = deposit32(env->hflags, HFLAGS_PMCC1, 1, hflags_pmcc1); - - pmu_update_summaries(env); + pmu_mmcr01_updated(env); /* Update cycle overflow timers with the current MMCR0 state */ pmu_update_overflow_timers(env); @@ -257,8 +263,7 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) env->spr[SPR_POWER_MMCR1] = value; - /* MMCR1 writes can change HFLAGS_INSN_CNT */ - pmu_update_summaries(env); + pmu_mmcr01_updated(env); } target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) @@ -287,8 +292,8 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE; env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; - /* Changing MMCR0_FC requires a new HFLAGS_INSN_CNT calc */ - pmu_update_summaries(env); + /* Changing MMCR0_FC requires summaries and hflags update */ + pmu_mmcr01_updated(env); /* * Delete all pending timers if we need to freeze @@ -299,6 +304,7 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) } if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE) { + /* These MMCR0 bits do not require summaries or hflags update. */ env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE; env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO; } diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index c0093e2219..775e640053 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -18,10 +18,10 @@ #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL void cpu_ppc_pmu_init(CPUPPCState *env); -void pmu_update_summaries(CPUPPCState *env); +void pmu_mmcr01_updated(CPUPPCState *env); #else static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } -static inline void pmu_update_summaries(CPUPPCState *env) { } +static inline void pmu_mmcr01_updated(CPUPPCState *env) { } #endif #endif From 82ce3d5614b2ef3a00b92ecbff074d5476eff285 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:43:12 +1000 Subject: [PATCH 242/412] target/ppc: PMU do not clear MMCR0[FCECE] on performance monitor alert FCECE does not get cleared according to the ISA v3.1B. Signed-off-by: Nicholas Piggin Reviewed-by: Daniel Henrique Barboza Message-Id: <20230530134313.387252-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/power8-pmu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index c4c331c6b5..af065115f2 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -289,7 +289,6 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) pmu_update_cycles(env); if (env->spr[SPR_POWER_MMCR0] & MMCR0_FCECE) { - env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE; env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; /* Changing MMCR0_FC requires summaries and hflags update */ From 2e9855555ebbbd4b22418b6bffce1f0f14234141 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:07:14 +1000 Subject: [PATCH 243/412] target/ppc: Fix msgclrp interrupt type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit msgclrp matches msgsndp and should clear PPC_INTERRUPT_DOORBELL. Signed-off-by: Nicholas Piggin Reviewed-by: Cédric Le Goater Message-Id: <20230530130714.373215-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/excp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 9ffcfe788a..de6ad121d2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -3071,7 +3071,7 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) return; } - ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_DOORBELL, 0); } /* From fd7abfab662a2bd8bbf63ad52d4b58243cd9c409 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:05:26 +1000 Subject: [PATCH 244/412] target/ppc: Support directed privileged doorbell interrupt (SDOOR) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BookS msgsndp instruction to self or DPDES register can cause SDOOR interrupts which crash QEMU with exception not implemented. Linux does not use msgsndp in SMT1, and KVM only uses DPDES to cause doorbells when emulating a SMT guest (which is not the default), so this has gone unnoticed. Signed-off-by: Nicholas Piggin Reviewed-by: Cédric Le Goater Message-Id: <20230530130526.372701-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/excp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index de6ad121d2..befa9aab7f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1542,6 +1542,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DSEG: /* Data segment exception */ case POWERPC_EXCP_ISEG: /* Instruction segment exception */ case POWERPC_EXCP_TRACE: /* Trace exception */ + case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */ break; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ msr |= env->error_code; @@ -1587,7 +1588,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */ case POWERPC_EXCP_HV_MAINT: /* Hypervisor Maintenance exception */ cpu_abort(cs, "%s exception not implemented\n", powerpc_excp_name(excp)); From c29b070418c79fa8e09d17e4b52f3ffddd393764 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:43:13 +1000 Subject: [PATCH 245/412] target/ppc: PMU implement PERFM interrupts The PMU raises a performance monitor exception (causing an interrupt when MSR[EE]=1) when MMCR0[PMAO] is set, and lowers it when clear. Wire this up and implement the interrupt delivery for books. Linux perf record can now collect PMI-driven samples. fire_PMC_interrupt is renamed to perfm_alert, which matches a bit closer to the new terminology used in the ISA and distinguishes the alert condition (e.g., counter overflow) from the PERFM (or EBB) interrupts. Signed-off-by: Nicholas Piggin Reviewed-by: Daniel Henrique Barboza Message-Id: <20230530134313.387252-2-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/excp_helper.c | 2 +- target/ppc/power8-pmu.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index befa9aab7f..8b95410c36 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1543,6 +1543,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_ISEG: /* Instruction segment exception */ case POWERPC_EXCP_TRACE: /* Trace exception */ case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */ + case POWERPC_EXCP_PERFM: /* Performance monitor interrupt */ break; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ msr |= env->error_code; @@ -1585,7 +1586,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) */ return; case POWERPC_EXCP_THERM: /* Thermal interrupt */ - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ case POWERPC_EXCP_MAINT: /* Maintenance exception */ case POWERPC_EXCP_HV_MAINT: /* Hypervisor Maintenance exception */ diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index af065115f2..7bb4bf81f7 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -84,8 +84,17 @@ static void pmu_update_summaries(CPUPPCState *env) void pmu_mmcr01_updated(CPUPPCState *env) { + PowerPCCPU *cpu = env_archcpu(env); + pmu_update_summaries(env); hreg_update_pmu_hflags(env); + + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAO) { + ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 1); + } else { + ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 0); + } + /* * Should this update overflow timers (if mmcr0 is updated) so they * get set in cpu_post_load? @@ -282,7 +291,7 @@ void helper_store_pmc(CPUPPCState *env, uint32_t sprn, uint64_t value) pmc_update_overflow_timer(env, sprn); } -static void fire_PMC_interrupt(PowerPCCPU *cpu) +static void perfm_alert(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; @@ -306,6 +315,7 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) /* These MMCR0 bits do not require summaries or hflags update. */ env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE; env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO; + ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 1); } raise_ebb_perfm_exception(env); @@ -314,20 +324,17 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) void helper_handle_pmc5_overflow(CPUPPCState *env) { env->spr[SPR_POWER_PMC5] = PMC_COUNTER_NEGATIVE_VAL; - fire_PMC_interrupt(env_archcpu(env)); + perfm_alert(env_archcpu(env)); } /* This helper assumes that the PMC is running. */ void helper_insns_inc(CPUPPCState *env, uint32_t num_insns) { bool overflow_triggered; - PowerPCCPU *cpu; overflow_triggered = pmu_increment_insns(env, num_insns); - if (overflow_triggered) { - cpu = env_archcpu(env); - fire_PMC_interrupt(cpu); + perfm_alert(env_archcpu(env)); } } @@ -335,7 +342,7 @@ static void cpu_ppc_pmu_timer_cb(void *opaque) { PowerPCCPU *cpu = opaque; - fire_PMC_interrupt(cpu); + perfm_alert(cpu); } void cpu_ppc_pmu_init(CPUPPCState *env) From 728fbfb57be5f4a3089a01f5c6ed72e0618b49ae Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:07 +0200 Subject: [PATCH 246/412] target/ppc: Remove single use function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_physical_address() function is a trivial wrapper of get_physical_address_wtlb() that is only used once. Remove it and call get_physical_address_wtlb() directly instead. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Message-Id: <302697d63d26caebefaeee1e45352145ebd0318a.1685448535.git.balaton@eik.bme.hu> Signed-off-by: Daniel Henrique Barboza --- target/ppc/mmu_helper.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 64e30435f5..c0c71a68ff 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -168,15 +168,6 @@ static void booke206_flush_tlb(CPUPPCState *env, int flags, tlb_flush(env_cpu(env)); } -static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type, - int type) -{ - return get_physical_address_wtlb(env, ctx, eaddr, access_type, type, 0); -} - - - /*****************************************************************************/ /* BATs management */ #if !defined(FLUSH_ALL_TLBS) @@ -643,7 +634,7 @@ target_ulong helper_rac(CPUPPCState *env, target_ulong addr) */ nb_BATs = env->nb_BATs; env->nb_BATs = 0; - if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0) { + if (get_physical_address_wtlb(env, &ctx, addr, 0, ACCESS_INT, 0) == 0) { ret = ctx.raddr; } env->nb_BATs = nb_BATs; From 62860c5feaf4eb53acafd10b238374d3ef21229a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:08 +0200 Subject: [PATCH 247/412] target/ppc: Remove "ext" parameter of ppcemb_tlb_check() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is only used by one caller so simplify function by removing this parameter and move the operation to the single place where it's used. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Message-Id: Signed-off-by: Daniel Henrique Barboza --- target/ppc/cpu.h | 3 +-- target/ppc/mmu_common.c | 21 +++++++++------------ target/ppc/mmu_helper.c | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 10c4ffa148..557e02e697 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1429,8 +1429,7 @@ int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, uint32_t pid); int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid, int ext, - int i); + target_ulong address, uint32_t pid, int i); hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 7235a4befe..21a353c51a 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -491,8 +491,7 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, /* Generic TLB check function for embedded PowerPC implementations */ int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid, int ext, - int i) + target_ulong address, uint32_t pid, int i) { target_ulong mask; @@ -514,11 +513,6 @@ int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } *raddrp = (tlb->RPN & mask) | (address & ~mask); - if (ext) { - /* Extend the physical address to 36 bits */ - *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - return 0; } @@ -536,7 +530,7 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, address, - env->spr[SPR_40x_PID], 0, i) < 0) { + env->spr[SPR_40x_PID], i) < 0) { continue; } zsel = (tlb->attr >> 4) & 0xF; @@ -598,20 +592,23 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, int prot2; if (ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID], - !env->nb_pids, i) >= 0) { + env->spr[SPR_BOOKE_PID], i) >= 0) { + if (!env->nb_pids) { + /* Extend the physical address to 36 bits */ + *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; + } goto found_tlb; } if (env->spr[SPR_BOOKE_PID1] && ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { + env->spr[SPR_BOOKE_PID1], i) >= 0) { goto found_tlb; } if (env->spr[SPR_BOOKE_PID2] && ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { + env->spr[SPR_BOOKE_PID2], i) >= 0) { goto found_tlb; } diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index c0c71a68ff..e7275eaec1 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -124,7 +124,7 @@ static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, ret = -1; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i) == 0) { ret = i; break; } From 753441c8898e5de41e6051f9d1244e98849bb866 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:09 +0200 Subject: [PATCH 248/412] target/ppc: Move ppcemb_tlb_search() to mmu_common.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is the only reason why ppcemb_tlb_check() is not static to mmu_common.c but it also better fits in mmu_common.c so move it there. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Message-Id: Signed-off-by: Daniel Henrique Barboza --- target/ppc/cpu.h | 4 +--- target/ppc/mmu_common.c | 22 +++++++++++++++++++++- target/ppc/mmu_helper.c | 21 --------------------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 557e02e697..8001582d52 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1427,9 +1427,7 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, target_ulong address, uint32_t pid); -int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int i); +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 21a353c51a..845eee4c6f 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -489,7 +489,7 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } /* Generic TLB check function for embedded PowerPC implementations */ -int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, +static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddrp, target_ulong address, uint32_t pid, int i) { @@ -516,6 +516,26 @@ int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, return 0; } +/* Generic TLB search function for PowerPC embedded implementations */ +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i, ret; + + /* Default return value is no match */ + ret = -1; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i) == 0) { + ret = i; + break; + } + } + + return ret; +} + static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong address, MMUAccessType access_type) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index e7275eaec1..d3ea7588f9 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -112,27 +112,6 @@ static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, env->last_way = way; } -/* Generic TLB search function for PowerPC embedded implementations */ -static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, - uint32_t pid) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret; - - /* Default return value is no match */ - ret = -1; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i) == 0) { - ret = i; - break; - } - } - - return ret; -} - /* Helpers specific to PowerPC 40x implementations */ static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) { From a1fa47fad12ea104b2d3f7f8b39f102bc16df539 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:10 +0200 Subject: [PATCH 249/412] target/ppc: Remove some unneded line breaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make lines shorter and fix indentation in some functions prototypes. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Message-Id: <70952ba2d82141db1cf5cfcf4b227402be575874.1685448535.git.balaton@eik.bme.hu> Signed-off-by: Daniel Henrique Barboza --- target/ppc/cpu.h | 8 +++----- target/ppc/mmu_common.c | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 8001582d52..c7c2a5534c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1424,12 +1424,10 @@ void store_booke_tsr(CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all(CPUPPCState *env); void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr); void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, target_ulong address, - uint32_t pid); +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid); int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, - ppcmas_tlb_t *tlb); +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 845eee4c6f..a84bc7de88 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -694,8 +694,7 @@ static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, - ppcmas_tlb_t *tlb) +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) { int tlbm_size; @@ -705,9 +704,8 @@ hwaddr booke206_tlb_to_page_size(CPUPPCState *env, } /* TLB check function for MAS based SoftTLBs */ -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, target_ulong address, - uint32_t pid) +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid) { hwaddr mask; uint32_t tlb_pid; From bb60364c202d0447fab78653b660a092b11378e4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:11 +0200 Subject: [PATCH 250/412] target/ppc: Simplify ppcemb_tlb_search() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No nead to store return value and break from loop when we can return directly. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Message-Id: Signed-off-by: Daniel Henrique Barboza --- target/ppc/mmu_common.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index a84bc7de88..ff7f987546 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -521,19 +521,15 @@ int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) { ppcemb_tlb_t *tlb; hwaddr raddr; - int i, ret; + int i; - /* Default return value is no match */ - ret = -1; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i) == 0) { - ret = i; - break; + return i; } } - - return ret; + return -1; } static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, From 2b23daa8ebaf184e89bd04eed21817620d600cc7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:12 +0200 Subject: [PATCH 251/412] target/ppc: Change ppcemb_tlb_check() to return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Message-Id: Signed-off-by: Daniel Henrique Barboza --- target/ppc/mmu_common.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ff7f987546..bd7d7d5257 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -489,15 +489,15 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } /* Generic TLB check function for embedded PowerPC implementations */ -static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int i) +static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int i) { target_ulong mask; /* Check valid flag */ if (!(tlb->prot & PAGE_VALID)) { - return -1; + return false; } mask = ~(tlb->size - 1); qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx @@ -506,14 +506,14 @@ static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, mask, (uint32_t)tlb->PID, tlb->prot); /* Check PID */ if (tlb->PID != 0 && tlb->PID != pid) { - return -1; + return false; } /* Check effective address */ if ((address & mask) != tlb->EPN) { - return -1; + return false; } *raddrp = (tlb->RPN & mask) | (address & ~mask); - return 0; + return true; } /* Generic TLB search function for PowerPC embedded implementations */ @@ -525,7 +525,7 @@ int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i) == 0) { + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) { return i; } } @@ -545,8 +545,8 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, pr = FIELD_EX64(env->msr, MSR, PR); for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, - env->spr[SPR_40x_PID], i) < 0) { + if (!ppcemb_tlb_check(env, tlb, &raddr, address, + env->spr[SPR_40x_PID], i)) { continue; } zsel = (tlb->attr >> 4) & 0xF; @@ -608,7 +608,7 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, int prot2; if (ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID], i) >= 0) { + env->spr[SPR_BOOKE_PID], i)) { if (!env->nb_pids) { /* Extend the physical address to 36 bits */ *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; @@ -618,13 +618,13 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, if (env->spr[SPR_BOOKE_PID1] && ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1], i) >= 0) { + env->spr[SPR_BOOKE_PID1], i)) { goto found_tlb; } if (env->spr[SPR_BOOKE_PID2] && ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2], i) >= 0) { + env->spr[SPR_BOOKE_PID2], i)) { goto found_tlb; } From a5436bc6ed4d385690546783f1799ea927f5ebd2 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 30 May 2023 15:28:13 +0200 Subject: [PATCH 252/412] target/ppc: Eliminate goto in mmubooke_check_tlb() Move out checking PID registers into a separate function which makes mmubooke_check_tlb() simpler and avoids using goto. Signed-off-by: BALATON Zoltan Reviewed-by: Daniel Henrique Barboza Message-Id: Signed-off-by: Daniel Henrique Barboza --- target/ppc/mmu_common.c | 50 +++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index bd7d7d5257..ae1db6e348 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -601,38 +601,40 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } +static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, target_ulong addr, int i) +{ + if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { + if (!env->nb_pids) { + /* Extend the physical address to 36 bits */ + *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; + } + return true; + } else if (!env->nb_pids) { + return false; + } + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { + return true; + } + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { + return true; + } + return false; +} + static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddr, int *prot, target_ulong address, MMUAccessType access_type, int i) { int prot2; - if (ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID], i)) { - if (!env->nb_pids) { - /* Extend the physical address to 36 bits */ - *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - goto found_tlb; + if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { + qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); + return -1; } - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1], i)) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2], i)) { - goto found_tlb; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - if (FIELD_EX64(env->msr, MSR, PR)) { prot2 = tlb->prot & 0xF; } else { From e025e8f5a8a7e32409bb4c7c509d752486113188 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 12:54:42 +1000 Subject: [PATCH 253/412] target/ppc: Fix lqarx to set cpu_reserve lqarx does not set cpu_reserve, which causes stqcx. to never succeed. Cc: qemu-stable@nongnu.org Fixes: 94bf2658676 ("target/ppc: Use atomic load for LQ and LQARX") Fixes: 57b38ffd0c6 ("target/ppc: Use tcg_gen_qemu_{ld,st}_i128 for LQARX, LQ, STQ") Signed-off-by: Nicholas Piggin Reviewed-by: Richard Henderson Message-Id: <20230605025445.161932-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 37fd431870..452439b729 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3765,6 +3765,7 @@ static void gen_lqarx(DisasContext *ctx) tcg_gen_qemu_ld_i128(t16, EA, ctx->mem_idx, DEF_MEMOP(MO_128 | MO_ALIGN)); tcg_gen_extr_i128_i64(lo, hi, t16); + tcg_gen_mov_tl(cpu_reserve, EA); tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); } From 392d328abe7534a6e852151740913e2cd50426bc Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 12:54:43 +1000 Subject: [PATCH 254/412] target/ppc: Ensure stcx size matches larx Differently-sized larx/stcx. pairs can succeed if the starting address matches. Add a check to require the size of stcx. exactly match the larx that established the reservation. Use the term "reserve_length" for this state, which matches the terminology used in the ISA. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin Message-Id: <20230605025445.161932-2-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/cpu.h | 5 +++-- target/ppc/cpu_init.c | 4 ++-- target/ppc/translate.c | 9 +++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c7c2a5534c..20508bac5e 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1114,8 +1114,9 @@ struct CPUArchState { target_ulong ov32; target_ulong ca32; - target_ulong reserve_addr; /* Reservation address */ - target_ulong reserve_val; /* Reservation value */ + target_ulong reserve_addr; /* Reservation address */ + target_ulong reserve_length; /* Reservation larx op size (bytes) */ + target_ulong reserve_val; /* Reservation value */ target_ulong reserve_val2; /* These are used in supervisor mode only */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 398f2d9966..d4ef074afb 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7392,8 +7392,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) } qemu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); } - qemu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", - env->reserve_addr); + qemu_fprintf(f, " ] RES %03x@" TARGET_FMT_lx "\n", + (int)env->reserve_length, env->reserve_addr); if (flags & CPU_DUMP_FPU) { for (i = 0; i < 32; i++) { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 452439b729..cf0bd79b8c 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -75,6 +75,7 @@ static TCGv cpu_cfar; #endif static TCGv cpu_xer, cpu_so, cpu_ov, cpu_ca, cpu_ov32, cpu_ca32; static TCGv cpu_reserve; +static TCGv cpu_reserve_length; static TCGv cpu_reserve_val; static TCGv cpu_reserve_val2; static TCGv cpu_fpscr; @@ -143,6 +144,10 @@ void ppc_translate_init(void) cpu_reserve = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, reserve_addr), "reserve_addr"); + cpu_reserve_length = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, + reserve_length), + "reserve_length"); cpu_reserve_val = tcg_global_mem_new(cpu_env, offsetof(CPUPPCState, reserve_val), "reserve_val"); @@ -3469,6 +3474,7 @@ static void gen_load_locked(DisasContext *ctx, MemOp memop) gen_addr_reg_index(ctx, t0); tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); tcg_gen_mov_tl(cpu_reserve, t0); + tcg_gen_movi_tl(cpu_reserve_length, memop_size(memop)); tcg_gen_mov_tl(cpu_reserve_val, gpr); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } @@ -3700,6 +3706,7 @@ static void gen_conditional_store(DisasContext *ctx, MemOp memop) gen_set_access_type(ctx, ACCESS_RES); gen_addr_reg_index(ctx, t0); tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, memop_size(memop), l1); t0 = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val, @@ -3766,6 +3773,7 @@ static void gen_lqarx(DisasContext *ctx) tcg_gen_extr_i128_i64(lo, hi, t16); tcg_gen_mov_tl(cpu_reserve, EA); + tcg_gen_movi_tl(cpu_reserve_length, 16); tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); } @@ -3791,6 +3799,7 @@ static void gen_stqcx_(DisasContext *ctx) gen_addr_reg_index(ctx, EA); tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, 16, lab_fail); cmp = tcg_temp_new_i128(); val = tcg_temp_new_i128(); From 2c901dca1863515bd71c3f351610f0698cb8f0b4 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 12:54:44 +1000 Subject: [PATCH 255/412] target/ppc: Remove larx/stcx. memory barrier semantics larx and stcx. are not defined to order any memory operations. Remove the barriers. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin Message-Id: <20230605025445.161932-3-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/translate.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cf0bd79b8c..cb4764476d 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3476,7 +3476,6 @@ static void gen_load_locked(DisasContext *ctx, MemOp memop) tcg_gen_mov_tl(cpu_reserve, t0); tcg_gen_movi_tl(cpu_reserve_length, memop_size(memop)); tcg_gen_mov_tl(cpu_reserve_val, gpr); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } #define LARX(name, memop) \ @@ -3720,11 +3719,6 @@ static void gen_conditional_store(DisasContext *ctx, MemOp memop) gen_set_label(l1); - /* - * Address mismatch implies failure. But we still need to provide - * the memory barrier semantics of the instruction. - */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); gen_set_label(l2); @@ -3828,11 +3822,6 @@ static void gen_stqcx_(DisasContext *ctx) tcg_gen_br(lab_over); gen_set_label(lab_fail); - /* - * Address mismatch implies failure. But we still need to provide - * the memory barrier semantics of the instruction. - */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); gen_set_label(lab_over); From 21ee07e7737a2dbba226a8d8192246e51855910d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 12:54:45 +1000 Subject: [PATCH 256/412] target/ppc: Rework store conditional to avoid branch Rework store conditional to avoid a branch in the success case. Change some of the variable names and layout while here so gen_conditional_store more closely matches gen_stqcx_. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin Message-Id: <20230605025445.161932-4-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- target/ppc/translate.c | 67 ++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cb4764476d..b591f2e496 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3697,31 +3697,32 @@ static void gen_stdat(DisasContext *ctx) static void gen_conditional_store(DisasContext *ctx, MemOp memop) { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - int reg = rS(ctx->opcode); - - gen_set_access_type(ctx, ACCESS_RES); - gen_addr_reg_index(ctx, t0); - tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, memop_size(memop), l1); + TCGLabel *lfail; + TCGv EA; + TCGv cr0; + TCGv t0; + int rs = rS(ctx->opcode); + lfail = gen_new_label(); + EA = tcg_temp_new(); + cr0 = tcg_temp_new(); t0 = tcg_temp_new(); + + tcg_gen_mov_tl(cr0, cpu_so); + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, EA); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lfail); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, memop_size(memop), lfail); + tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val, - cpu_gpr[reg], ctx->mem_idx, + cpu_gpr[rs], ctx->mem_idx, DEF_MEMOP(memop) | MO_ALIGN); tcg_gen_setcond_tl(TCG_COND_EQ, t0, t0, cpu_reserve_val); tcg_gen_shli_tl(t0, t0, CRF_EQ_BIT); - tcg_gen_or_tl(t0, t0, cpu_so); - tcg_gen_trunc_tl_i32(cpu_crf[0], t0); - tcg_gen_br(l2); + tcg_gen_or_tl(cr0, cr0, t0); - gen_set_label(l1); - - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - - gen_set_label(l2); + gen_set_label(lfail); + tcg_gen_trunc_tl_i32(cpu_crf[0], cr0); tcg_gen_movi_tl(cpu_reserve, -1); } @@ -3775,25 +3776,26 @@ static void gen_lqarx(DisasContext *ctx) /* stqcx. */ static void gen_stqcx_(DisasContext *ctx) { - TCGLabel *lab_fail, *lab_over; - int rs = rS(ctx->opcode); + TCGLabel *lfail; TCGv EA, t0, t1; + TCGv cr0; TCGv_i128 cmp, val; + int rs = rS(ctx->opcode); if (unlikely(rs & 1)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } - lab_fail = gen_new_label(); - lab_over = gen_new_label(); - - gen_set_access_type(ctx, ACCESS_RES); + lfail = gen_new_label(); EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + cr0 = tcg_temp_new(); - tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, 16, lab_fail); + tcg_gen_mov_tl(cr0, cpu_so); + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, EA); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lfail); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, 16, lfail); cmp = tcg_temp_new_i128(); val = tcg_temp_new_i128(); @@ -3816,15 +3818,10 @@ static void gen_stqcx_(DisasContext *ctx) tcg_gen_setcondi_tl(TCG_COND_EQ, t0, t0, 0); tcg_gen_shli_tl(t0, t0, CRF_EQ_BIT); - tcg_gen_or_tl(t0, t0, cpu_so); - tcg_gen_trunc_tl_i32(cpu_crf[0], t0); + tcg_gen_or_tl(cr0, cr0, t0); - tcg_gen_br(lab_over); - gen_set_label(lab_fail); - - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - - gen_set_label(lab_over); + gen_set_label(lfail); + tcg_gen_trunc_tl_i32(cpu_crf[0], cr0); tcg_gen_movi_tl(cpu_reserve, -1); } #endif /* defined(TARGET_PPC64) */ From 09d2db9f46e38e2da990df8ad914d735d764251a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:12:12 +1000 Subject: [PATCH 257/412] target/ppc: Fix decrementer time underflow and infinite timer loop It is possible to store a very large value to the decrementer that it does not raise the decrementer exception so the timer is scheduled, but the next time value wraps and is treated as in the past. This can occur if (u64)-1 is stored on a zero-triggered exception, or (u64)-1 is stored twice on an underflow-triggered exception, for example. If such a value is set in DECAR, it gets stored to the decrementer by the timer function, which then immediately causes another timer, which hangs QEMU. Clamp the decrementer to the implemented width, and use that as the value for the timer calculation, effectively preventing this overflow. Reported-by: sdicaro@DDCI.com Signed-off-by: Nicholas Piggin Reviewed-by: Daniel Henrique Barboza Message-Id: <20230530131214.373524-1-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/ppc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 4e816c68c7..d80b0adc6c 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -798,6 +798,8 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, int64_t signed_decr; /* Truncate value to decr_width and sign extend for simplicity */ + value = extract64(value, 0, nr_bits); + decr = extract64(decr, 0, nr_bits); signed_value = sextract64(value, 0, nr_bits); signed_decr = sextract64(decr, 0, nr_bits); From 17dd1354c1d1aba9caf4af01e11aa7dbe128474f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 30 May 2023 23:12:13 +1000 Subject: [PATCH 258/412] target/ppc: Decrementer fix BookE semantics The decrementer store function has logic that short-cuts the timer if a very small value is stored (0, 1, or 2) and raises an interrupt directly. There are two problem with this on BookE. First is that BookE says a decrementer interrupt should not be raised on a store of 0, only of a decrement from 1. Second is that raising the irq directly will bypass the auto-reload logic in the booke decr timer function, breaking autoreload when 1 or 2 is stored. Fix this by removing that small-value special case. It makes this tricky logic even more difficult to reason about, and it hardly matters for performance. Cc: sdicaro@DDCI.com Signed-off-by: Nicholas Piggin Reviewed-by: Daniel Henrique Barboza Message-Id: <20230530131214.373524-2-npiggin@gmail.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/ppc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index d80b0adc6c..1b1220c423 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -811,11 +811,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, } /* - * Going from 2 -> 1, 1 -> 0 or 0 -> -1 is the event to generate a DEC - * interrupt. - * - * If we get a really small DEC value, we can assume that by the time we - * handled it we should inject an interrupt already. + * Going from 1 -> 0 or 0 -> -1 is the event to generate a DEC interrupt. * * On MSB level based DEC implementations the MSB always means the interrupt * is pending, so raise it on those. @@ -823,8 +819,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers * an edge interrupt, so raise it here too. */ - if ((value < 3) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || + if (((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 && signed_decr >= 0)) { (*raise_excp)(cpu); From 8e67403a2cdde2dd43b43683f4478f3c8ed07cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 23 May 2023 08:15:46 +0200 Subject: [PATCH 259/412] hw/ppc/openpic: Do not open-code ROUND_UP() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While reviewing, the ROUND_UP() macro is easier to figure out. Besides, the comment confirms we want to round up here. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Message-Id: <20230523061546.49031-1-philmd@linaro.org> Signed-off-by: Daniel Henrique Barboza --- include/hw/ppc/openpic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h index ebdaf8a493..bae8dafe16 100644 --- a/include/hw/ppc/openpic.h +++ b/include/hw/ppc/openpic.h @@ -55,7 +55,7 @@ typedef enum IRQType { * Round up to the nearest 64 IRQs so that the queue length * won't change when moving between 32 and 64 bit hosts. */ -#define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63) +#define IRQQUEUE_SIZE_BITS ROUND_UP(OPENPIC_MAX_IRQ, 64) typedef struct IRQQueue { unsigned long *queue; From 12cae32fe1a18a4e89472e7f92b06a4eb1beb671 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 6 Jun 2023 21:28:02 +0200 Subject: [PATCH 260/412] tests/avocado/tuxrun_baselines: Fix ppc64 tests for binaries without slirp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ppc64 tuxrun tests are currently failing if "slirp" has been disabled in the binary since they are using "-netdev user" now. We have to skip the test if this network backend is missing. Fixes: 6ee3624236 ("improve code coverage for ppc64") Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Acked-by: Alex Bennée Message-Id: <20230606192802.666000-1-thuth@redhat.com> Signed-off-by: Daniel Henrique Barboza --- tests/avocado/tuxrun_baselines.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py index 3a46e7a745..e12250eabb 100644 --- a/tests/avocado/tuxrun_baselines.py +++ b/tests/avocado/tuxrun_baselines.py @@ -184,6 +184,7 @@ class TuxRunBaselineTest(QemuSystemTest): def ppc64_common_tuxrun(self, sums, prefix): # add device args to command line. + self.require_netdev('user') self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', '-device', 'virtio-net,netdev=vnet') self.vm.add_args('-netdev', '{"type":"user","id":"hostnet0"}', From 8a15ccee4d59b74be551dc7956d78bde2a52764a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 7 Jun 2023 00:02:00 +0200 Subject: [PATCH 261/412] target/ppc: Implement gathering irq statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Count exceptions which can be queried with info irq monitor command. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230606220200.7EBCC74635C@zero.eik.bme.hu> Signed-off-by: Daniel Henrique Barboza --- target/ppc/cpu.h | 1 + target/ppc/cpu_init.c | 18 ++++++++++++++++++ target/ppc/excp_helper.c | 1 + 3 files changed, 20 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 20508bac5e..0ee2adc105 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1195,6 +1195,7 @@ struct CPUArchState { int error_code; uint32_t pending_interrupts; #if !defined(CONFIG_USER_ONLY) + uint64_t excp_stats[POWERPC_EXCP_NB]; /* * This is the IRQ controller, which is implementation dependent and only * relevant when emulating a complete machine. Note that this isn't used diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index d4ef074afb..9f97222655 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -48,6 +48,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" +#include "hw/intc/intc.h" #endif /* #define PPC_DEBUG_SPR */ @@ -7123,6 +7124,16 @@ static bool ppc_cpu_is_big_endian(CPUState *cs) return !FIELD_EX64(env->msr, MSR, LE); } +static bool ppc_get_irq_stats(InterruptStatsProvider *obj, + uint64_t **irq_counts, unsigned int *nb_irqs) +{ + CPUPPCState *env = &POWERPC_CPU(obj)->env; + + *irq_counts = env->excp_stats; + *nb_irqs = ARRAY_SIZE(env->excp_stats); + return true; +} + #ifdef CONFIG_TCG static void ppc_cpu_exec_enter(CPUState *cs) { @@ -7286,6 +7297,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_write_register = ppc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &ppc_sysemu_ops; + INTERRUPT_STATS_PROVIDER_CLASS(oc)->get_statistics = ppc_get_irq_stats; #endif cc->gdb_num_core_regs = 71; @@ -7323,6 +7335,12 @@ static const TypeInfo ppc_cpu_type_info = { .abstract = true, .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, +#ifndef CONFIG_USER_ONLY + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, +#endif }; #ifndef CONFIG_USER_ONLY diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 8b95410c36..12d8a7257b 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1655,6 +1655,7 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), excp, env->error_code); + env->excp_stats[excp]++; switch (env->excp_model) { case POWERPC_EXCP_40x: From 9ec08f3569be3bc8bfd4d9b8b0445b9136910661 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 30 May 2023 12:20:41 +0200 Subject: [PATCH 262/412] hw/ppc/Kconfig: MAC_NEWWORLD should always select USB_OHCI_PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PowerMacs have an OHCI controller soldered on the motherboard, so this should always be enabled for the "mac99" machine. This fixes the problem that QEMU aborts when the user tries to run the "mac99" machine with a build that has been compiled with the "--without-default-devices" configure switch. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Message-Id: <20230530102041.55527-1-thuth@redhat.com> Signed-off-by: Daniel Henrique Barboza --- hw/ppc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index a689d9b219..5dfbf47ef5 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -115,6 +115,7 @@ config MAC_NEWWORLD select MAC_PMU select UNIN_PCI select FW_CFG_PPC + select USB_OHCI_PCI config E500 bool From bc0ec52eb258e55aa8a4a4ab89cb5c8ad49b30ee Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 27 Apr 2023 17:57:07 -0300 Subject: [PATCH 263/412] target/riscv/vector_helper.c: skip set tail when vta is zero The function is a no-op if 'vta' is zero but we're still doing a lot of stuff in this function regardless. vext_set_elems_1s() will ignore every single time (since vta is zero) and we just wasted time. Skip it altogether in this case. Aside from the code simplification there's a noticeable emulation performance gain by doing it. For a regular C binary that does a vectors operation like this: ======= #define SZ 10000000 int main () { int *a = malloc (SZ * sizeof (int)); int *b = malloc (SZ * sizeof (int)); int *c = malloc (SZ * sizeof (int)); for (int i = 0; i < SZ; i++) c[i] = a[i] + b[i]; return c[SZ - 1]; } ======= Emulating it with qemu-riscv64 and RVV takes ~0.3 sec: $ time ~/work/qemu/build/qemu-riscv64 \ -cpu rv64,debug=false,vext_spec=v1.0,v=true,vlen=128 ./foo.out real 0m0.303s user 0m0.281s sys 0m0.023s With this skip we take ~0.275 sec: $ time ~/work/qemu/build/qemu-riscv64 \ -cpu rv64,debug=false,vext_spec=v1.0,v=true,vlen=128 ./foo.out real 0m0.274s user 0m0.252s sys 0m0.019s This performance gain adds up fast when executing heavy benchmarks like SPEC. Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Reviewed-by: Palmer Dabbelt Reviewed-by: Weiwei Li Message-Id: <20230427205708.246679-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index f4d0438988..8e6c99e573 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -268,12 +268,17 @@ static void vext_set_tail_elems_1s(CPURISCVState *env, target_ulong vl, void *vd, uint32_t desc, uint32_t nf, uint32_t esz, uint32_t max_elems) { - uint32_t total_elems = vext_get_total_elems(env, desc, esz); - uint32_t vlenb = riscv_cpu_cfg(env)->vlen >> 3; + uint32_t total_elems, vlenb, registers_used; uint32_t vta = vext_vta(desc); - uint32_t registers_used; int k; + if (vta == 0) { + return; + } + + total_elems = vext_get_total_elems(env, desc, esz); + vlenb = riscv_cpu_cfg(env)->vlen >> 3; + for (k = 0; k < nf; ++k) { vext_set_elems_1s(vd, vta, (k * max_elems + vl) * esz, (k * max_elems + max_elems) * esz); From 6672e29d3bc8e1a855a9ee8209c7315b8bc94d19 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 10 May 2023 11:00:40 +0800 Subject: [PATCH 264/412] target/riscv: Move zc* out of the experimental properties Zc* extensions (version 1.0) are ratified. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Message-Id: <20230510030040.20528-1-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index db0875fb43..99ed9cb80e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1571,6 +1571,14 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_BOOL("zmmul", RISCVCPU, cfg.ext_zmmul, false), + DEFINE_PROP_BOOL("zca", RISCVCPU, cfg.ext_zca, false), + DEFINE_PROP_BOOL("zcb", RISCVCPU, cfg.ext_zcb, false), + DEFINE_PROP_BOOL("zcd", RISCVCPU, cfg.ext_zcd, false), + DEFINE_PROP_BOOL("zce", RISCVCPU, cfg.ext_zce, false), + DEFINE_PROP_BOOL("zcf", RISCVCPU, cfg.ext_zcf, false), + DEFINE_PROP_BOOL("zcmp", RISCVCPU, cfg.ext_zcmp, false), + DEFINE_PROP_BOOL("zcmt", RISCVCPU, cfg.ext_zcmt, false), + /* Vendor-specific custom extensions */ DEFINE_PROP_BOOL("xtheadba", RISCVCPU, cfg.ext_xtheadba, false), DEFINE_PROP_BOOL("xtheadbb", RISCVCPU, cfg.ext_xtheadbb, false), @@ -1588,14 +1596,6 @@ static Property riscv_cpu_extensions[] = { /* These are experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-zicond", RISCVCPU, cfg.ext_zicond, false), - DEFINE_PROP_BOOL("x-zca", RISCVCPU, cfg.ext_zca, false), - DEFINE_PROP_BOOL("x-zcb", RISCVCPU, cfg.ext_zcb, false), - DEFINE_PROP_BOOL("x-zcd", RISCVCPU, cfg.ext_zcd, false), - DEFINE_PROP_BOOL("x-zce", RISCVCPU, cfg.ext_zce, false), - DEFINE_PROP_BOOL("x-zcf", RISCVCPU, cfg.ext_zcf, false), - DEFINE_PROP_BOOL("x-zcmp", RISCVCPU, cfg.ext_zcmp, false), - DEFINE_PROP_BOOL("x-zcmt", RISCVCPU, cfg.ext_zcmt, false), - /* ePMP 0.9.3 */ DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), DEFINE_PROP_BOOL("x-smaia", RISCVCPU, cfg.ext_smaia, false), From d63be18490e1931479ba6dc424de9c568d1419d8 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:04 -0300 Subject: [PATCH 265/412] target/riscv/cpu.c: add riscv_cpu_validate_v() The RVV verification will error out if fails and it's being done at the end of riscv_cpu_validate_set_extensions(), after we've already set some extensions that are dependent on RVV. Let's put it in its own function and do it earlier. Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 89 +++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 99ed9cb80e..1558a0b93f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -834,6 +834,46 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) } } +static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, + Error **errp) +{ + int vext_version = VEXT_VERSION_1_00_0; + + if (!is_power_of_2(cfg->vlen)) { + error_setg(errp, "Vector extension VLEN must be power of 2"); + return; + } + if (cfg->vlen > RV_VLEN_MAX || cfg->vlen < 128) { + error_setg(errp, + "Vector extension implementation only supports VLEN " + "in the range [128, %d]", RV_VLEN_MAX); + return; + } + if (!is_power_of_2(cfg->elen)) { + error_setg(errp, "Vector extension ELEN must be power of 2"); + return; + } + if (cfg->elen > 64 || cfg->elen < 8) { + error_setg(errp, + "Vector extension implementation only supports ELEN " + "in the range [8, 64]"); + return; + } + if (cfg->vext_spec) { + if (!g_strcmp0(cfg->vext_spec, "v1.0")) { + vext_version = VEXT_VERSION_1_00_0; + } else { + error_setg(errp, "Unsupported vector spec version '%s'", + cfg->vext_spec); + return; + } + } else { + qemu_log("vector version is not specified, " + "use the default value v1.0\n"); + } + set_vext_version(env, vext_version); +} + /* * Check consistency between chosen extensions while setting * cpu->cfg accordingly. @@ -841,6 +881,7 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) { CPURISCVState *env = &cpu->env; + Error *local_err = NULL; /* Do some ISA extension error checking */ if (riscv_has_ext(env, RVG) && @@ -909,8 +950,14 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - /* The V vector extension depends on the Zve64d extension */ if (riscv_has_ext(env, RVV)) { + riscv_cpu_validate_v(env, &cpu->cfg, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + /* The V vector extension depends on the Zve64d extension */ cpu->cfg.ext_zve64d = true; } @@ -1045,46 +1092,6 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_zksed = true; cpu->cfg.ext_zksh = true; } - - if (riscv_has_ext(env, RVV)) { - int vext_version = VEXT_VERSION_1_00_0; - if (!is_power_of_2(cpu->cfg.vlen)) { - error_setg(errp, - "Vector extension VLEN must be power of 2"); - return; - } - if (cpu->cfg.vlen > RV_VLEN_MAX || cpu->cfg.vlen < 128) { - error_setg(errp, - "Vector extension implementation only supports VLEN " - "in the range [128, %d]", RV_VLEN_MAX); - return; - } - if (!is_power_of_2(cpu->cfg.elen)) { - error_setg(errp, - "Vector extension ELEN must be power of 2"); - return; - } - if (cpu->cfg.elen > 64 || cpu->cfg.elen < 8) { - error_setg(errp, - "Vector extension implementation only supports ELEN " - "in the range [8, 64]"); - return; - } - if (cpu->cfg.vext_spec) { - if (!g_strcmp0(cpu->cfg.vext_spec, "v1.0")) { - vext_version = VEXT_VERSION_1_00_0; - } else { - error_setg(errp, - "Unsupported vector spec version '%s'", - cpu->cfg.vext_spec); - return; - } - } else { - qemu_log("vector version is not specified, " - "use the default value v1.0\n"); - } - set_vext_version(env, vext_version); - } } #ifndef CONFIG_USER_ONLY From 2238c9d196e90a991e83919802158f4232202039 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:05 -0300 Subject: [PATCH 266/412] target/riscv/cpu.c: remove set_vext_version() This setter is doing nothing else but setting env->vext_ver. Assign the value directly. Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1558a0b93f..13d725dc20 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -252,11 +252,6 @@ static void set_priv_version(CPURISCVState *env, int priv_ver) env->priv_ver = priv_ver; } -static void set_vext_version(CPURISCVState *env, int vext_ver) -{ - env->vext_ver = vext_ver; -} - #ifndef CONFIG_USER_ONLY static uint8_t satp_mode_from_str(const char *satp_mode_str) { @@ -871,7 +866,7 @@ static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, qemu_log("vector version is not specified, " "use the default value v1.0\n"); } - set_vext_version(env, vext_version); + env->vext_ver = vext_version; } /* From 8c6eeb508a98b185fac71c4dad523763ab779a83 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:06 -0300 Subject: [PATCH 267/412] target/riscv/cpu.c: remove set_priv_version() The setter is doing nothing special. Just set env->priv_ver directly. Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 13d725dc20..9fc9d1cc0f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -247,11 +247,6 @@ static void set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext) env->misa_ext_mask = env->misa_ext = ext; } -static void set_priv_version(CPURISCVState *env, int priv_ver) -{ - env->priv_ver = priv_ver; -} - #ifndef CONFIG_USER_ONLY static uint8_t satp_mode_from_str(const char *satp_mode_str) { @@ -350,7 +345,7 @@ static void riscv_any_cpu_init(Object *obj) VM_1_10_SV32 : VM_1_10_SV57); #endif - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_1_12_0; } #if defined(TARGET_RISCV64) @@ -361,7 +356,7 @@ static void rv64_base_cpu_init(Object *obj) set_misa(env, MXL_RV64, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif @@ -371,7 +366,7 @@ static void rv64_sifive_u_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39); #endif @@ -383,7 +378,7 @@ static void rv64_sifive_e_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); @@ -396,7 +391,7 @@ static void rv64_thead_c906_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_11_0); + env->priv_ver = PRIV_VERSION_1_11_0; cpu->cfg.ext_zfh = true; cpu->cfg.mmu = true; @@ -467,7 +462,7 @@ static void rv128_base_cpu_init(Object *obj) set_misa(env, MXL_RV128, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif @@ -480,7 +475,7 @@ static void rv32_base_cpu_init(Object *obj) set_misa(env, MXL_RV32, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); #endif @@ -490,7 +485,7 @@ static void rv32_sifive_u_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); #endif @@ -502,7 +497,7 @@ static void rv32_sifive_e_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); @@ -515,7 +510,7 @@ static void rv32_ibex_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_11_0); + env->priv_ver = PRIV_VERSION_1_11_0; cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); @@ -529,7 +524,7 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + env->priv_ver = PRIV_VERSION_1_10_0; cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); @@ -1211,7 +1206,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } if (priv_version >= PRIV_VERSION_1_10_0) { - set_priv_version(env, priv_version); + env->priv_ver = priv_version; } riscv_cpu_validate_misa_priv(env, &local_err); From b9a2b98e172336e8ec464b3b63bf0dedba944502 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:07 -0300 Subject: [PATCH 268/412] target/riscv: add PRIV_VERSION_LATEST All these generic CPUs are using the latest priv available, at this moment PRIV_VERSION_1_12_0: - riscv_any_cpu_init() - rv32_base_cpu_init() - rv64_base_cpu_init() - rv128_base_cpu_init() Create a new PRIV_VERSION_LATEST enum and use it in those cases. I'll make it easier to update everything at once when a new priv version is available. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Richard Henderson Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 8 ++++---- target/riscv/cpu.h | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9fc9d1cc0f..a0c4acfb47 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -345,7 +345,7 @@ static void riscv_any_cpu_init(Object *obj) VM_1_10_SV32 : VM_1_10_SV57); #endif - env->priv_ver = PRIV_VERSION_1_12_0; + env->priv_ver = PRIV_VERSION_LATEST; } #if defined(TARGET_RISCV64) @@ -356,7 +356,7 @@ static void rv64_base_cpu_init(Object *obj) set_misa(env, MXL_RV64, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_1_12_0; + env->priv_ver = PRIV_VERSION_LATEST; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif @@ -462,7 +462,7 @@ static void rv128_base_cpu_init(Object *obj) set_misa(env, MXL_RV128, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_1_12_0; + env->priv_ver = PRIV_VERSION_LATEST; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif @@ -475,7 +475,7 @@ static void rv32_base_cpu_init(Object *obj) set_misa(env, MXL_RV32, 0); riscv_cpu_add_user_properties(obj); /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_1_12_0; + env->priv_ver = PRIV_VERSION_LATEST; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index de7e43126a..15423585d0 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -61,6 +61,8 @@ enum { PRIV_VERSION_1_10_0 = 0, PRIV_VERSION_1_11_0, PRIV_VERSION_1_12_0, + + PRIV_VERSION_LATEST = PRIV_VERSION_1_12_0, }; #define VEXT_VERSION_1_00_0 0x00010000 From 61a33ea95a7efb7f9a211b1a812e9bb6e7525f1d Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 10:57:08 -0300 Subject: [PATCH 269/412] target/riscv: Mask the implicitly enabled extensions in isa_string based on priv version Using implicitly enabled extensions such as Zca/Zcf/Zcd instead of their super extensions can simplify the extension related check. However, they may have higher priv version than their super extensions. So we should mask them in the isa_string based on priv version to make them invisible to user if the specified priv version is lower than their minimal priv version. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-Id: <20230517135714.211809-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a0c4acfb47..81a785d525 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1758,7 +1758,8 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int i; for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { + if (cpu->env.priv_ver >= isa_edata_arr[i].min_version && + isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL); g_free(old); old = new; From d33e39f995c377fee61281ddf44cd793e1a45625 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 10:57:09 -0300 Subject: [PATCH 270/412] target/riscv: Update check for Zca/Zcf/Zcd Even though Zca/Zcf/Zcd can be included by C/F/D, however, their priv version is higher than the priv version of C/F/D. So if we use check for them instead of check for C/F/D totally, it will trigger new problem when we try to disable the extensions based on the configured priv version. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvd.c.inc | 12 +++++++----- target/riscv/insn_trans/trans_rvf.c.inc | 14 ++++++++------ target/riscv/insn_trans/trans_rvi.c.inc | 5 +++-- target/riscv/translate.c | 5 +++-- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 2c51e01c40..6bdb55ef43 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -31,9 +31,11 @@ } \ } while (0) -#define REQUIRE_ZCD(ctx) do { \ - if (!ctx->cfg_ptr->ext_zcd) { \ - return false; \ +#define REQUIRE_ZCD_OR_DC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcd) { \ + if (!has_ext(ctx, RVD) || !has_ext(ctx, RVC)) { \ + return false; \ + } \ } \ } while (0) @@ -67,13 +69,13 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) static bool trans_c_fld(DisasContext *ctx, arg_fld *a) { - REQUIRE_ZCD(ctx); + REQUIRE_ZCD_OR_DC(ctx); return trans_fld(ctx, a); } static bool trans_c_fsd(DisasContext *ctx, arg_fsd *a) { - REQUIRE_ZCD(ctx); + REQUIRE_ZCD_OR_DC(ctx); return trans_fsd(ctx, a); } diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index b2de4fcf3f..c47138575a 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -30,10 +30,12 @@ } \ } while (0) -#define REQUIRE_ZCF(ctx) do { \ - if (!ctx->cfg_ptr->ext_zcf) { \ - return false; \ - } \ +#define REQUIRE_ZCF_OR_FC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcf) { \ + if (!has_ext(ctx, RVF) || !has_ext(ctx, RVC)) { \ + return false; \ + } \ + } \ } while (0) static bool trans_flw(DisasContext *ctx, arg_flw *a) @@ -69,13 +71,13 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) static bool trans_c_flw(DisasContext *ctx, arg_flw *a) { - REQUIRE_ZCF(ctx); + REQUIRE_ZCF_OR_FC(ctx); return trans_flw(ctx, a); } static bool trans_c_fsw(DisasContext *ctx, arg_fsw *a) { - REQUIRE_ZCF(ctx); + REQUIRE_ZCF_OR_FC(ctx); return trans_fsw(ctx, a); } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 2031e9931e..d794247f40 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -56,7 +56,7 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); gen_set_pc(ctx, cpu_pc); - if (!ctx->cfg_ptr->ext_zca) { + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { TCGv t0 = tcg_temp_new(); misaligned = gen_new_label(); @@ -169,7 +169,8 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) gen_set_label(l); /* branch taken */ - if (!ctx->cfg_ptr->ext_zca && ((ctx->base.pc_next + a->imm) & 0x3)) { + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca && + ((ctx->base.pc_next + a->imm) & 0x3)) { /* misaligned */ gen_exception_inst_addr_mis(ctx); } else { diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 933b11c50d..29f1fb3995 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -551,7 +551,7 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) /* check misaligned: */ next_pc = ctx->base.pc_next + imm; - if (!ctx->cfg_ptr->ext_zca) { + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { if ((next_pc & 0x3) != 0) { gen_exception_inst_addr_mis(ctx); return; @@ -1125,7 +1125,8 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) * The Zca extension is added as way to refer to instructions in the C * extension that do not include the floating-point loads and stores */ - if (ctx->cfg_ptr->ext_zca && decode_insn16(ctx, opcode)) { + if ((has_ext(ctx, RVC) || ctx->cfg_ptr->ext_zca) && + decode_insn16(ctx, opcode)) { return; } } else { From bd30559568607324f467bff66d8644fc08cb4729 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:10 -0300 Subject: [PATCH 271/412] target/riscv/cpu.c: add priv_spec validate/disable_exts helpers We're doing env->priv_spec validation and assignment at the start of riscv_cpu_realize(), which is fine, but then we're doing a force disable on extensions that aren't compatible with the priv version. This second step is being done too early. The disabled extensions might be re-enabled again in riscv_cpu_validate_set_extensions() by accident. A better place to put this code is at the end of riscv_cpu_validate_set_extensions() after all the validations are completed. Add a new helper, riscv_cpu_disable_priv_spec_isa_exts(), to disable the extesions after the validation is done. While we're at it, create a riscv_cpu_validate_priv_spec() helper to host all env->priv_spec related validation to unclog riscv_cpu_realize a bit. Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 91 ++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 81a785d525..a0589abb16 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -864,6 +864,52 @@ static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, env->vext_ver = vext_version; } +static void riscv_cpu_validate_priv_spec(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + int priv_version = -1; + + if (cpu->cfg.priv_spec) { + if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { + priv_version = PRIV_VERSION_1_12_0; + } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { + priv_version = PRIV_VERSION_1_11_0; + } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { + priv_version = PRIV_VERSION_1_10_0; + } else { + error_setg(errp, + "Unsupported privilege spec version '%s'", + cpu->cfg.priv_spec); + return; + } + + env->priv_ver = priv_version; + } +} + +static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) +{ + CPURISCVState *env = &cpu->env; + int i; + + /* Force disable extensions if priv spec version does not match */ + for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { + if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) && + (env->priv_ver < isa_edata_arr[i].min_version)) { + isa_ext_update_enabled(cpu, &isa_edata_arr[i], false); +#ifndef CONFIG_USER_ONLY + warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx + " because privilege spec version does not match", + isa_edata_arr[i].name, env->mhartid); +#else + warn_report("disabling %s extension because " + "privilege spec version does not match", + isa_edata_arr[i].name); +#endif + } + } +} + /* * Check consistency between chosen extensions while setting * cpu->cfg accordingly. @@ -1082,6 +1128,12 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_zksed = true; cpu->cfg.ext_zksh = true; } + + /* + * Disable isa extensions based on priv spec after we + * validated and set everything we need. + */ + riscv_cpu_disable_priv_spec_isa_exts(cpu); } #ifndef CONFIG_USER_ONLY @@ -1181,7 +1233,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); CPUClass *cc = CPU_CLASS(mcc); - int i, priv_version = -1; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -1190,23 +1241,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } - if (cpu->cfg.priv_spec) { - if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { - priv_version = PRIV_VERSION_1_12_0; - } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { - priv_version = PRIV_VERSION_1_11_0; - } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { - priv_version = PRIV_VERSION_1_10_0; - } else { - error_setg(errp, - "Unsupported privilege spec version '%s'", - cpu->cfg.priv_spec); - return; - } - } - - if (priv_version >= PRIV_VERSION_1_10_0) { - env->priv_ver = priv_version; + riscv_cpu_validate_priv_spec(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; } riscv_cpu_validate_misa_priv(env, &local_err); @@ -1215,23 +1253,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } - /* Force disable extensions if priv spec version does not match */ - for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) && - (env->priv_ver < isa_edata_arr[i].min_version)) { - isa_ext_update_enabled(cpu, &isa_edata_arr[i], false); -#ifndef CONFIG_USER_ONLY - warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx - " because privilege spec version does not match", - isa_edata_arr[i].name, env->mhartid); -#else - warn_report("disabling %s extension because " - "privilege spec version does not match", - isa_edata_arr[i].name); -#endif - } - } - if (cpu->cfg.epmp && !cpu->cfg.pmp) { /* * Enhanced PMP should only be available From f5664064cc808dc5057627fbb0e5d68a394f2fc1 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:11 -0300 Subject: [PATCH 272/412] target/riscv/cpu.c: add riscv_cpu_validate_misa_mxl() Let's remove more code that is open coded in riscv_cpu_realize() and put it into a helper. Let's also add an error message instead of just asserting out if env->misa_mxl_max != env->misa_mlx. Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 50 ++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a0589abb16..89a1a25812 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -910,6 +910,33 @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) } } +static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + CPUClass *cc = CPU_CLASS(mcc); + CPURISCVState *env = &cpu->env; + + /* Validate that MISA_MXL is set properly. */ + switch (env->misa_mxl_max) { +#ifdef TARGET_RISCV64 + case MXL_RV64: + case MXL_RV128: + cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; + break; +#endif + case MXL_RV32: + cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; + break; + default: + g_assert_not_reached(); + } + + if (env->misa_mxl_max != env->misa_mxl) { + error_setg(errp, "misa_mxl_max must be equal to misa_mxl"); + return; + } +} + /* * Check consistency between chosen extensions while setting * cpu->cfg accordingly. @@ -1232,7 +1259,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) RISCVCPU *cpu = RISCV_CPU(dev); CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); - CPUClass *cc = CPU_CLASS(mcc); Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -1241,6 +1267,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } + riscv_cpu_validate_misa_mxl(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + riscv_cpu_validate_priv_spec(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1269,22 +1301,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } #endif /* CONFIG_USER_ONLY */ - /* Validate that MISA_MXL is set properly. */ - switch (env->misa_mxl_max) { -#ifdef TARGET_RISCV64 - case MXL_RV64: - case MXL_RV128: - cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; - break; -#endif - case MXL_RV32: - cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; - break; - default: - g_assert_not_reached(); - } - assert(env->misa_mxl_max == env->misa_mxl); - riscv_cpu_validate_set_extensions(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); From e2fa85f42f0bddcfcb2249b1757a9e1a209ae4e3 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:12 -0300 Subject: [PATCH 273/412] target/riscv/cpu.c: validate extensions before riscv_timer_init() There is no need to init timers if we're not even sure that our extensions are valid. Execute riscv_cpu_validate_set_extensions() before riscv_timer_init(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-10-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 89a1a25812..9f2c8fa7c6 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1294,13 +1294,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } - -#ifndef CONFIG_USER_ONLY - if (cpu->cfg.ext_sstc) { - riscv_timer_init(cpu); - } -#endif /* CONFIG_USER_ONLY */ - riscv_cpu_validate_set_extensions(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1308,6 +1301,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } #ifndef CONFIG_USER_ONLY + if (cpu->cfg.ext_sstc) { + riscv_timer_init(cpu); + } + if (cpu->cfg.pmu_num) { if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, From 7f0bdfb5bfc2b6f1199f8b09e3b6dc9015b7b9c3 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:13 -0300 Subject: [PATCH 274/412] target/riscv/cpu.c: remove cfg setup from riscv_cpu_init() We have 4 config settings being done in riscv_cpu_init(): ext_ifencei, ext_icsr, mmu and pmp. This is also the constructor of the "riscv-cpu" device, which happens to be the parent device of every RISC-V cpu. The result is that these 4 configs are being set every time, and every other CPU should always account for them. CPUs such as sifive_e need to disable settings that aren't enabled simply because the parent class happens to be enabling it. Moving all configurations from the parent class to each CPU will centralize the config of each CPU into its own init(), which is clearer than having to account to whatever happens to be set in the parent device. These settings are also being set in register_cpu_props() when no 'misa_ext' is set, so for these CPUs we don't need changes. Named CPUs will receive all cfgs that the parent were setting into their init(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230517135714.211809-11-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 59 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9f2c8fa7c6..778801dffc 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -332,7 +332,8 @@ static void set_satp_mode_default_map(RISCVCPU *cpu) static void riscv_any_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; #if defined(TARGET_RISCV32) set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVU); #elif defined(TARGET_RISCV64) @@ -346,6 +347,12 @@ static void riscv_any_cpu_init(Object *obj) #endif env->priv_ver = PRIV_VERSION_LATEST; + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } #if defined(TARGET_RISCV64) @@ -364,12 +371,19 @@ static void rv64_base_cpu_init(Object *obj) static void rv64_sifive_u_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } static void rv64_sifive_e_cpu_init(Object *obj) @@ -379,10 +393,14 @@ static void rv64_sifive_e_cpu_init(Object *obj) set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU); env->priv_ver = PRIV_VERSION_1_10_0; - cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } static void rv64_thead_c906_cpu_init(Object *obj) @@ -410,6 +428,9 @@ static void rv64_thead_c906_cpu_init(Object *obj) #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_SV39); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.pmp = true; } static void rv64_veyron_v1_cpu_init(Object *obj) @@ -483,12 +504,19 @@ static void rv32_base_cpu_init(Object *obj) static void rv32_sifive_u_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); env->priv_ver = PRIV_VERSION_1_10_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } static void rv32_sifive_e_cpu_init(Object *obj) @@ -498,10 +526,14 @@ static void rv32_sifive_e_cpu_init(Object *obj) set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU); env->priv_ver = PRIV_VERSION_1_10_0; - cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } static void rv32_ibex_cpu_init(Object *obj) @@ -511,11 +543,15 @@ static void rv32_ibex_cpu_init(Object *obj) set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); env->priv_ver = PRIV_VERSION_1_11_0; - cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif cpu->cfg.epmp = true; + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } static void rv32_imafcu_nommu_cpu_init(Object *obj) @@ -525,10 +561,14 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU); env->priv_ver = PRIV_VERSION_1_10_0; - cpu->cfg.mmu = false; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_ifencei = true; + cpu->cfg.ext_icsr = true; + cpu->cfg.pmp = true; } #endif @@ -1441,11 +1481,6 @@ static void riscv_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); - cpu->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - cpu_set_cpustate_pointers(cpu); #ifndef CONFIG_USER_ONLY From faf3b5d86ff3b0349ded42f8ef3240d1960b6a1a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 17 May 2023 10:57:14 -0300 Subject: [PATCH 275/412] target/riscv: rework write_misa() write_misa() must use as much common logic as possible. We want to open code just the bits that are exclusive to the CSR write operation and TCG internals. Our validation is done with riscv_cpu_validate_set_extensions(), but we need a small tweak first. When enabling RVG we're doing: env->misa_ext |= RVI | RVM | RVA | RVF | RVD; env->misa_ext_mask = env->misa_ext; This works fine for realize() time but this can potentially overwrite env->misa_ext_mask if we reutilize the function for write_misa(). Instead of doing misa_ext_mask = misa_ext, sum up the RVG extensions in misa_ext_mask as well. This won't change realize() time behavior (misa_ext_mask will be == misa_ext) and will ensure that write_misa() won't change misa_ext_mask by accident. After that, rewrite write_misa() to work as follows: - mask the write using misa_ext_mask to avoid enabling unsupported extensions; - suppress RVC if the next insn isn't aligned; - disable RVG if any of RVG dependencies are being disabled by the user; - assign env->misa_ext and run riscv_cpu_validate_set_extensions(). On error, rollback env->misa_ext to its original value, logging a GUEST_ERROR to inform the user about the failed write; - handle RVF and MSTATUS_FS and continue as usual. Let's keep write_misa() as experimental for now until this logic gains enough mileage. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Weiwei Li Signed-off-by: Daniel Henrique Barboza Message-Id: <20230517135714.211809-12-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 4 ++-- target/riscv/cpu.h | 1 + target/riscv/csr.c | 51 ++++++++++++++++++++++------------------------ 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 778801dffc..e67c78f860 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -981,7 +981,7 @@ static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp) * Check consistency between chosen extensions while setting * cpu->cfg accordingly. */ -static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) +void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) { CPURISCVState *env = &cpu->env; Error *local_err = NULL; @@ -997,7 +997,7 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_ifencei = true; env->misa_ext |= RVI | RVM | RVA | RVF | RVD; - env->misa_ext_mask = env->misa_ext; + env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD; } if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) { diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 15423585d0..1f39edc687 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -548,6 +548,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool probe, uintptr_t retaddr); char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(void); +void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 4451bd1263..cf7da4f87f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1387,39 +1387,18 @@ static RISCVException read_misa(CPURISCVState *env, int csrno, static RISCVException write_misa(CPURISCVState *env, int csrno, target_ulong val) { + RISCVCPU *cpu = env_archcpu(env); + uint32_t orig_misa_ext = env->misa_ext; + Error *local_err = NULL; + if (!riscv_cpu_cfg(env)->misa_w) { /* drop write to misa */ return RISCV_EXCP_NONE; } - /* 'I' or 'E' must be present */ - if (!(val & (RVI | RVE))) { - /* It is not, drop write to misa */ - return RISCV_EXCP_NONE; - } - - /* 'E' excludes all other extensions */ - if (val & RVE) { - /* - * when we support 'E' we can do "val = RVE;" however - * for now we just drop writes if 'E' is present. - */ - return RISCV_EXCP_NONE; - } - - /* - * misa.MXL writes are not supported by QEMU. - * Drop writes to those bits. - */ - /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* 'D' depends on 'F', so clear 'D' if 'F' is not present */ - if ((val & RVD) && !(val & RVF)) { - val &= ~RVD; - } - /* * Suppress 'C' if next instruction is not aligned * TODO: this should check next_pc @@ -1428,18 +1407,36 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, val &= ~RVC; } + /* Disable RVG if any of its dependencies are disabled */ + if (!(val & RVI && val & RVM && val & RVA && + val & RVF && val & RVD)) { + val &= ~RVG; + } + /* If nothing changed, do nothing. */ if (val == env->misa_ext) { return RISCV_EXCP_NONE; } - if (!(val & RVF)) { + env->misa_ext = val; + riscv_cpu_validate_set_extensions(cpu, &local_err); + if (local_err != NULL) { + /* Rollback on validation error */ + qemu_log_mask(LOG_GUEST_ERROR, "Unable to write MISA ext value " + "0x%x, keeping existing MISA ext 0x%x\n", + env->misa_ext, orig_misa_ext); + + env->misa_ext = orig_misa_ext; + + return RISCV_EXCP_NONE; + } + + if (!(env->misa_ext & RVF)) { env->mstatus &= ~MSTATUS_FS; } /* flush translation cache */ tb_flush(env_cpu(env)); - env->misa_ext = val; env->xl = riscv_cpu_mxl(env); return RISCV_EXCP_NONE; } From dc7b599332c26065cd9ff1f2f2cf3ed580ca3dfd Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:08 +0800 Subject: [PATCH 276/412] target/riscv: Update pmp_get_tlb_size() PMP entries before (including) the matched PMP entry may only cover partial of the TLB page, and this may split the page into regions with different permissions. Such as for PMP0 (0x80000008~0x8000000F, R) and PMP1 (0x80000000~ 0x80000FFF, RWX), write access to 0x80000000 will match PMP1. However we cannot cache the translation result in the TLB since this will make the write access to 0x80000008 bypass the check of PMP0. So we should check all of them instead of the matched PMP entry in pmp_get_tlb_size() and set the tlb_size to 1 in this case. Set tlb_size to TARGET_PAGE_SIZE if PMP is not support or there is no PMP rules. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 7 ++-- target/riscv/pmp.c | 73 ++++++++++++++++++++++++++++++--------- target/riscv/pmp.h | 3 +- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 56381aaf26..065b433b88 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -715,11 +715,8 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, } *prot = pmp_priv_to_page_prot(pmp_priv); - if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) { - target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); - target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; - - *tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea); + if (tlb_size != NULL) { + *tlb_size = pmp_get_tlb_size(env, addr); } return TRANSLATE_SUCCESS; diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 1f5aca42e8..2bc924340a 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -601,28 +601,67 @@ target_ulong mseccfg_csr_read(CPURISCVState *env) } /* - * Calculate the TLB size if the start address or the end address of - * PMP entry is presented in the TLB page. + * Calculate the TLB size. + * It's possible that PMP regions only cover partial of the TLB page, and + * this may split the page into regions with different permissions. + * For example if PMP0 is (0x80000008~0x8000000F, R) and PMP1 is (0x80000000 + * ~0x80000FFF, RWX), then region 0x80000008~0x8000000F has R permission, and + * the other regions in this page have RWX permissions. + * A write access to 0x80000000 will match PMP1. However we cannot cache the + * translation result in the TLB since this will make the write access to + * 0x80000008 bypass the check of PMP0. + * To avoid this we return a size of 1 (which means no caching) if the PMP + * region only covers partial of the TLB page. */ -target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, - target_ulong tlb_sa, target_ulong tlb_ea) +target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr) { - target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa; - target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea; + target_ulong pmp_sa; + target_ulong pmp_ea; + target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); + target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; + int i; - if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) { + /* + * If PMP is not supported or there are no PMP rules, the TLB page will not + * be split into regions with different permissions by PMP so we set the + * size to TARGET_PAGE_SIZE. + */ + if (!riscv_cpu_cfg(env)->pmp || !pmp_get_num_rules(env)) { return TARGET_PAGE_SIZE; - } else { - /* - * At this point we have a tlb_size that is the smallest possible size - * That fits within a TARGET_PAGE_SIZE and the PMP region. - * - * If the size is less then TARGET_PAGE_SIZE we drop the size to 1. - * This means the result isn't cached in the TLB and is only used for - * a single translation. - */ - return 1; } + + for (i = 0; i < MAX_RISCV_PMPS; i++) { + if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) { + continue; + } + + pmp_sa = env->pmp_state.addr[i].sa; + pmp_ea = env->pmp_state.addr[i].ea; + + /* + * Only the first PMP entry that covers (whole or partial of) the TLB + * page really matters: + * If it covers the whole TLB page, set the size to TARGET_PAGE_SIZE, + * since the following PMP entries have lower priority and will not + * affect the permissions of the page. + * If it only covers partial of the TLB page, set the size to 1 since + * the allowed permissions of the region may be different from other + * region of the page. + */ + if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) { + return TARGET_PAGE_SIZE; + } else if ((pmp_sa >= tlb_sa && pmp_sa <= tlb_ea) || + (pmp_ea >= tlb_sa && pmp_ea <= tlb_ea)) { + return 1; + } + } + + /* + * If no PMP entry matches the TLB page, the TLB page will also not be + * split into regions with different permissions by PMP so we set the size + * to TARGET_PAGE_SIZE. + */ + return TARGET_PAGE_SIZE; } /* diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index b296ea1fc6..0a7e24750b 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -76,8 +76,7 @@ int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs, target_ulong mode); -target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, - target_ulong tlb_sa, target_ulong tlb_ea); +target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr); void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_nums(CPURISCVState *env); uint32_t pmp_get_num_rules(CPURISCVState *env); From bfc7ee1224152782e6624fcc859ffa52b73c1531 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:09 +0800 Subject: [PATCH 277/412] target/riscv: Move pmp_get_tlb_size apart from get_physical_address_pmp pmp_get_tlb_size can be separated from get_physical_address_pmp and is only needed when ret == TRANSLATE_SUCCESS. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 065b433b88..52d0b043df 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -688,14 +688,11 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * * @env: CPURISCVState * @prot: The returned protection attributes - * @tlb_size: TLB page size containing addr. It could be modified after PMP - * permission checking. NULL if not set TLB page for addr. * @addr: The physical address to be checked permission * @access_type: The type of MMU access * @mode: Indicates current privilege level. */ -static int get_physical_address_pmp(CPURISCVState *env, int *prot, - target_ulong *tlb_size, hwaddr addr, +static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, int size, MMUAccessType access_type, int mode) { @@ -715,9 +712,6 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, } *prot = pmp_priv_to_page_prot(pmp_priv); - if (tlb_size != NULL) { - *tlb_size = pmp_get_tlb_size(env, addr); - } return TRANSLATE_SUCCESS; } @@ -906,7 +900,7 @@ restart: } int pmp_prot; - int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr, + int pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, sizeof(target_ulong), MMU_DATA_LOAD, PRV_S); if (pmp_ret != TRANSLATE_SUCCESS) { @@ -1302,8 +1296,9 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, prot &= prot2; if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + ret = get_physical_address_pmp(env, &prot_pmp, pa, size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" @@ -1335,8 +1330,9 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, __func__, address, ret, pa, prot); if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + ret = get_physical_address_pmp(env, &prot_pmp, pa, size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" From 093ce837e1facb4168fbe24974ee92220cdd1907 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:10 +0800 Subject: [PATCH 278/412] target/riscv: Make the short cut really work in pmp_hart_has_privs Return the result directly for short cut, since We needn't do the following check on the PMP entries if there is no PMP rules. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 2bc924340a..3c90562d55 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -316,6 +316,7 @@ int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, allowed_privs, mode)) { ret = MAX_RISCV_PMPS; } + return ret; } if (size == 0) { From e9c39713ea09faa74f502e32d71d52c1c2e8ccf1 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:11 +0800 Subject: [PATCH 279/412] target/riscv: Change the return type of pmp_hart_has_privs() to bool We no longer need the pmp_index for matched PMP entry now. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-5-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 8 ++++---- target/riscv/pmp.c | 32 +++++++++++++------------------- target/riscv/pmp.h | 8 ++++---- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 52d0b043df..35ddd0caac 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -697,16 +697,16 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, int mode) { pmp_priv_t pmp_priv; - int pmp_index = -1; + bool pmp_has_privs; if (!riscv_cpu_cfg(env)->pmp) { *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TRANSLATE_SUCCESS; } - pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type, - &pmp_priv, mode); - if (pmp_index < 0) { + pmp_has_privs = pmp_hart_has_privs(env, addr, size, 1 << access_type, + &pmp_priv, mode); + if (!pmp_has_privs) { *prot = 0; return TRANSLATE_PMP_FAIL; } diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 3c90562d55..0684047b86 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -296,27 +296,23 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, /* * Check if the address has required RWX privs to complete desired operation - * Return PMP rule index if a pmp rule match - * Return MAX_RISCV_PMPS if default match - * Return negtive value if no match + * Return true if a pmp rule match or default match + * Return false if no match */ -int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, - pmp_priv_t *allowed_privs, target_ulong mode) +bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, + target_ulong size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, target_ulong mode) { int i = 0; - int ret = -1; + bool ret = false; int pmp_size = 0; target_ulong s = 0; target_ulong e = 0; /* Short cut if no rules */ if (0 == pmp_get_num_rules(env)) { - if (pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode)) { - ret = MAX_RISCV_PMPS; - } - return ret; + return pmp_hart_has_privs_default(env, addr, size, privs, + allowed_privs, mode); } if (size == 0) { @@ -345,7 +341,7 @@ int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, if ((s + e) == 1) { qemu_log_mask(LOG_GUEST_ERROR, "pmp violation - access is partially inside\n"); - ret = -1; + ret = false; break; } @@ -453,17 +449,15 @@ int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, * defined with PMP must be used. We shouldn't fallback on * finding default privileges. */ - ret = i; + ret = true; break; } } /* No rule matched */ - if (ret == -1) { - if (pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode)) { - ret = MAX_RISCV_PMPS; - } + if (!ret) { + ret = pmp_hart_has_privs_default(env, addr, size, privs, + allowed_privs, mode); } return ret; diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index 0a7e24750b..cf5c99f8e6 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -72,10 +72,10 @@ target_ulong mseccfg_csr_read(CPURISCVState *env); void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val); target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); -int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, - pmp_priv_t *allowed_privs, - target_ulong mode); +bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, + target_ulong size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, + target_ulong mode); target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr); void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_nums(CPURISCVState *env); From b84ffd6e74b5e4e7ddbb492bc9c8d798c8261703 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:12 +0800 Subject: [PATCH 280/412] target/riscv: Make RLB/MML/MMWP bits writable only when Smepmp is enabled RLB/MML/MMWP bits in mseccfg CSR are introduced by Smepmp extension. So they can only be writable and set to 1s when cfg.epmp is true. Then we also need't check on epmp in pmp_hart_has_privs_default(). Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-6-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 50 ++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 0684047b86..9a6e04cda0 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -243,30 +243,28 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, { bool ret; - if (riscv_cpu_cfg(env)->epmp) { - if (MSECCFG_MMWP_ISSET(env)) { - /* - * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set - * so we default to deny all, even for M-mode. - */ + if (MSECCFG_MMWP_ISSET(env)) { + /* + * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set + * so we default to deny all, even for M-mode. + */ + *allowed_privs = 0; + return false; + } else if (MSECCFG_MML_ISSET(env)) { + /* + * The Machine Mode Lockdown (mseccfg.MML) bit is set + * so we can only execute code in M-mode with an applicable + * rule. Other modes are disabled. + */ + if (mode == PRV_M && !(privs & PMP_EXEC)) { + ret = true; + *allowed_privs = PMP_READ | PMP_WRITE; + } else { + ret = false; *allowed_privs = 0; - return false; - } else if (MSECCFG_MML_ISSET(env)) { - /* - * The Machine Mode Lockdown (mseccfg.MML) bit is set - * so we can only execute code in M-mode with an applicable - * rule. Other modes are disabled. - */ - if (mode == PRV_M && !(privs & PMP_EXEC)) { - ret = true; - *allowed_privs = PMP_READ | PMP_WRITE; - } else { - ret = false; - *allowed_privs = 0; - } - - return ret; } + + return ret; } if (!riscv_cpu_cfg(env)->pmp || (mode == PRV_M)) { @@ -580,8 +578,12 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) } } - /* Sticky bits */ - val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + if (riscv_cpu_cfg(env)->epmp) { + /* Sticky bits */ + val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + } else { + val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB); + } env->mseccfg = val; } From 97ec5aef082d8b40a0a37d4d6274ffe165ef27b7 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:13 +0800 Subject: [PATCH 281/412] target/riscv: Remove unused paramters in pmp_hart_has_privs_default() The addr and size parameters in pmp_hart_has_privs_default() are unused. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-7-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 9a6e04cda0..2403039133 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -236,8 +236,7 @@ static int pmp_is_in_range(CPURISCVState *env, int pmp_index, /* * Check if the address has required RWX privs when no PMP entry is matched. */ -static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, +static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs, pmp_priv_t *allowed_privs, target_ulong mode) { @@ -309,8 +308,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* Short cut if no rules */ if (0 == pmp_get_num_rules(env)) { - return pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode); + return pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } if (size == 0) { @@ -454,8 +452,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* No rule matched */ if (!ret) { - ret = pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode); + ret = pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } return ret; From 37e79058031b0490734f724edf4e1691f6b3601e Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:14 +0800 Subject: [PATCH 282/412] target/riscv: Flush TLB when MMWP or MML bits are changed MMWP and MML bits may affect the allowed privs of PMP entries and the default privs, both of which may change the allowed privs of exsited TLB entries. So we need flush TLB when they are changed. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-8-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 2403039133..4d62dfc732 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -578,6 +578,9 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) if (riscv_cpu_cfg(env)->epmp) { /* Sticky bits */ val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) { + tlb_flush(env_cpu(env)); + } } else { val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB); } From 2b3e127856b8a0a6a3a6dfa815f3165810436483 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:15 +0800 Subject: [PATCH 283/412] target/riscv: Update the next rule addr in pmpaddr_csr_write() Currently only the rule addr of the same index of pmpaddr is updated when pmpaddr CSR is modified. However, the rule addr of next PMP entry may also be affected if its A field is PMP_AMATCH_TOR. So we should also update it in this case. Write to pmpaddr CSR will not affect the rule nums, So we needn't update call pmp_update_rule_nums() in pmpaddr_csr_write(). Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-9-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 4d62dfc732..48a3e44a77 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -507,6 +507,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val) { trace_pmpaddr_csr_write(env->mhartid, addr_index, val); + bool is_next_cfg_tor = false; if (addr_index < MAX_RISCV_PMPS) { /* @@ -515,9 +516,9 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, */ if (addr_index + 1 < MAX_RISCV_PMPS) { uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; + is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); - if (pmp_cfg & PMP_LOCK && - PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg)) { + if (pmp_cfg & PMP_LOCK && is_next_cfg_tor) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - pmpcfg + 1 locked\n"); return; @@ -526,7 +527,10 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, if (!pmp_is_locked(env, addr_index)) { env->pmp_state.pmp[addr_index].addr_reg = val; - pmp_update_rule(env, addr_index); + pmp_update_rule_addr(env, addr_index); + if (is_next_cfg_tor) { + pmp_update_rule_addr(env, addr_index + 1); + } } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - locked\n"); From 7c4c31f6d977c54751ca33ac5d77583faf20872d Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:16 +0800 Subject: [PATCH 284/412] target/riscv: Flush TLB when pmpaddr is updated TLB should be flushed not only for pmpcfg csr changes, but also for pmpaddr csr changes. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-Id: <20230517091519.34439-10-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 48a3e44a77..e0acee7a15 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -531,6 +531,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, if (is_next_cfg_tor) { pmp_update_rule_addr(env, addr_index + 1); } + tlb_flush(env_cpu(env)); } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - locked\n"); From e924074f1367f6b808aedd3359fb00f261adb405 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:17 +0800 Subject: [PATCH 285/412] target/riscv: Flush TLB only when pmpcfg/pmpaddr really changes TLB needn't be flushed when pmpcfg/pmpaddr don't changes. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-Id: <20230517091519.34439-11-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index e0acee7a15..3c12690565 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -26,7 +26,7 @@ #include "trace.h" #include "exec/exec-all.h" -static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, +static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, uint8_t val); static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); @@ -83,7 +83,7 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) * Accessor to set the cfg reg for a specific PMP/HART * Bounds checks and relevant lock bit. */ -static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) +static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { if (pmp_index < MAX_RISCV_PMPS) { bool locked = true; @@ -119,14 +119,17 @@ static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) if (locked) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); - } else { + } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { env->pmp_state.pmp[pmp_index].cfg_reg = val; pmp_update_rule(env, pmp_index); + return true; } } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - out of bounds\n"); } + + return false; } static void pmp_decode_napot(target_ulong a, target_ulong *sa, @@ -467,16 +470,19 @@ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, int i; uint8_t cfg_val; int pmpcfg_nums = 2 << riscv_cpu_mxl(env); + bool modified = false; trace_pmpcfg_csr_write(env->mhartid, reg_index, val); for (i = 0; i < pmpcfg_nums; i++) { cfg_val = (val >> 8 * i) & 0xff; - pmp_write_cfg(env, (reg_index * 4) + i, cfg_val); + modified |= pmp_write_cfg(env, (reg_index * 4) + i, cfg_val); } /* If PMP permission of any addr has been changed, flush TLB pages. */ - tlb_flush(env_cpu(env)); + if (modified) { + tlb_flush(env_cpu(env)); + } } @@ -526,12 +532,14 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, } if (!pmp_is_locked(env, addr_index)) { - env->pmp_state.pmp[addr_index].addr_reg = val; - pmp_update_rule_addr(env, addr_index); - if (is_next_cfg_tor) { - pmp_update_rule_addr(env, addr_index + 1); + if (env->pmp_state.pmp[addr_index].addr_reg != val) { + env->pmp_state.pmp[addr_index].addr_reg = val; + pmp_update_rule_addr(env, addr_index); + if (is_next_cfg_tor) { + pmp_update_rule_addr(env, addr_index + 1); + } + tlb_flush(env_cpu(env)); } - tlb_flush(env_cpu(env)); } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - locked\n"); From 1b63f2fee68e28566c218bdf34988f8c5978b1e4 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:18 +0800 Subject: [PATCH 286/412] target/riscv: Separate pmp_update_rule() in pmpcfg_csr_write Use pmp_update_rule_addr() and pmp_update_rule_nums() separately to update rule nums only once for each pmpcfg_csr_write. Then remove pmp_update_rule() since it become unused. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-12-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 3c12690565..37e9985d6a 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -29,7 +29,6 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, uint8_t val); static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); /* * Accessor method to extract address matching type 'a field' from cfg reg @@ -121,7 +120,7 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { env->pmp_state.pmp[pmp_index].cfg_reg = val; - pmp_update_rule(env, pmp_index); + pmp_update_rule_addr(env, pmp_index); return true; } } else { @@ -209,18 +208,6 @@ void pmp_update_rule_nums(CPURISCVState *env) } } -/* - * Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' - * end address values. - * This function is called relatively infrequently whereas the check that - * an address is within a pmp rule is called often, so optimise that one - */ -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) -{ - pmp_update_rule_addr(env, pmp_index); - pmp_update_rule_nums(env); -} - static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) { @@ -481,6 +468,7 @@ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, /* If PMP permission of any addr has been changed, flush TLB pages. */ if (modified) { + pmp_update_rule_nums(env); tlb_flush(env_cpu(env)); } } From 89fbbaddfb33556f3f669d40f917c65e75ea6768 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 17 May 2023 17:15:19 +0800 Subject: [PATCH 287/412] target/riscv: Deny access if access is partially inside the PMP entry Access will fail if access is partially inside the PMP entry. However,only setting ret = false doesn't really mean pmp violation since pmp_hart_has_privs_default() may return true at the end of pmp_hart_has_privs(). Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Message-Id: <20230517091519.34439-13-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 37e9985d6a..418738afd8 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -327,8 +327,8 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, if ((s + e) == 1) { qemu_log_mask(LOG_GUEST_ERROR, "pmp violation - access is partially inside\n"); - ret = false; - break; + *allowed_privs = 0; + return false; } /* fully inside */ From 9b29697fefa59431984aed9118e6fc062da0ee10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 20 May 2023 07:45:06 +0200 Subject: [PATCH 288/412] hw/riscv/opentitan: Rename machine_[class]_init() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow QOM style which declares FOO_init() as instance initializer and FOO_class_init() as class initializer: rename the OpenTitan machine class/instance init() accordingly. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230520054510.68822-2-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index bc678766e7..2d21ee39c5 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -75,7 +75,7 @@ static const MemMapEntry ibex_memmap[] = { [IBEX_DEV_FLASH_VIRTUAL] = { 0x80000000, 0x80000 }, }; -static void opentitan_board_init(MachineState *machine) +static void opentitan_machine_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); const MemMapEntry *memmap = ibex_memmap; @@ -108,17 +108,17 @@ static void opentitan_board_init(MachineState *machine) } } -static void opentitan_machine_init(MachineClass *mc) +static void opentitan_machine_class_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with OpenTitan"; - mc->init = opentitan_board_init; + mc->init = opentitan_machine_init; mc->max_cpus = 1; mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } -DEFINE_MACHINE("opentitan", opentitan_machine_init) +DEFINE_MACHINE("opentitan", opentitan_machine_class_init) static void lowrisc_ibex_soc_init(Object *obj) { From e0782b11bd7ef0c8e5cb9b5e83c32736a143deee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 20 May 2023 07:45:07 +0200 Subject: [PATCH 289/412] hw/riscv/opentitan: Declare QOM types using DEFINE_TYPES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple QOM types are registered in the same file, it is simpler to use the the DEFINE_TYPES() macro. Replace the type_init() / type_register_static() combination. This is in preparation of adding the OpenTitan machine type to this array in a pair of commits. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230520054510.68822-3-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 2d21ee39c5..294955eeea 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -320,17 +320,14 @@ static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static const TypeInfo lowrisc_ibex_soc_type_info = { - .name = TYPE_RISCV_IBEX_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(LowRISCIbexSoCState), - .instance_init = lowrisc_ibex_soc_init, - .class_init = lowrisc_ibex_soc_class_init, +static const TypeInfo open_titan_types[] = { + { + .name = TYPE_RISCV_IBEX_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(LowRISCIbexSoCState), + .instance_init = lowrisc_ibex_soc_init, + .class_init = lowrisc_ibex_soc_class_init, + } }; -static void lowrisc_ibex_soc_register_types(void) -{ - type_register_static(&lowrisc_ibex_soc_type_info); -} - -type_init(lowrisc_ibex_soc_register_types) +DEFINE_TYPES(open_titan_types) From 264495f9486ac17ea0275b0de1510b5de32d142b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 20 May 2023 07:45:08 +0200 Subject: [PATCH 290/412] hw/riscv/opentitan: Add TYPE_OPENTITAN_MACHINE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QOM type names are usually defined as TYPE_FOO. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230520054510.68822-4-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 2 +- include/hw/riscv/opentitan.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 294955eeea..7d7159ea30 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -118,7 +118,7 @@ static void opentitan_machine_class_init(MachineClass *mc) mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } -DEFINE_MACHINE("opentitan", opentitan_machine_class_init) +DEFINE_MACHINE(TYPE_OPENTITAN_MACHINE, opentitan_machine_class_init) static void lowrisc_ibex_soc_init(Object *obj) { diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index c40b05052a..fd70226ed8 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -53,6 +53,8 @@ struct LowRISCIbexSoCState { MemoryRegion flash_alias; }; +#define TYPE_OPENTITAN_MACHINE "opentitan" + typedef struct OpenTitanState { /*< private >*/ SysBusDevice parent_obj; From 8696b74a6fed86a9d2bd7e947d0490c2459a8aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 20 May 2023 07:45:09 +0200 Subject: [PATCH 291/412] hw/riscv/opentitan: Explicit machine type definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the DEFINE_MACHINE() macro, converting the class_init() handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230520054510.68822-5-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 10 +++++++--- include/hw/riscv/opentitan.h | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 7d7159ea30..9535308197 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -108,8 +108,10 @@ static void opentitan_machine_init(MachineState *machine) } } -static void opentitan_machine_class_init(MachineClass *mc) +static void opentitan_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "RISC-V Board compatible with OpenTitan"; mc->init = opentitan_machine_init; mc->max_cpus = 1; @@ -118,8 +120,6 @@ static void opentitan_machine_class_init(MachineClass *mc) mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } -DEFINE_MACHINE(TYPE_OPENTITAN_MACHINE, opentitan_machine_class_init) - static void lowrisc_ibex_soc_init(Object *obj) { LowRISCIbexSoCState *s = RISCV_IBEX_SOC(obj); @@ -327,6 +327,10 @@ static const TypeInfo open_titan_types[] = { .instance_size = sizeof(LowRISCIbexSoCState), .instance_init = lowrisc_ibex_soc_init, .class_init = lowrisc_ibex_soc_class_init, + }, { + .name = TYPE_OPENTITAN_MACHINE, + .parent = TYPE_MACHINE, + .class_init = opentitan_machine_class_init, } }; diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index fd70226ed8..806ff73528 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -24,6 +24,7 @@ #include "hw/char/ibex_uart.h" #include "hw/timer/ibex_timer.h" #include "hw/ssi/ibex_spi_host.h" +#include "hw/boards.h" #include "qom/object.h" #define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc" @@ -53,7 +54,7 @@ struct LowRISCIbexSoCState { MemoryRegion flash_alias; }; -#define TYPE_OPENTITAN_MACHINE "opentitan" +#define TYPE_OPENTITAN_MACHINE MACHINE_TYPE_NAME("opentitan") typedef struct OpenTitanState { /*< private >*/ From a828ba9d46972e32c135f386dd08c02aa7eb8f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 20 May 2023 07:45:10 +0200 Subject: [PATCH 292/412] hw/riscv/opentitan: Correct OpenTitanState parent type/size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenTitanState is the 'machine' (or 'board') state: it isn't a SysBus device, but inherits from the MachineState type. Correct the instance size. Doing so we avoid leaking an OpenTitanState pointer in opentitan_machine_init(). Fixes: fe0fe4735e ("riscv: Initial commit of OpenTitan machine") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230520054510.68822-6-philmd@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/opentitan.c | 3 ++- include/hw/riscv/opentitan.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 9535308197..6a2fcc4ade 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -78,8 +78,8 @@ static const MemMapEntry ibex_memmap[] = { static void opentitan_machine_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); + OpenTitanState *s = OPENTITAN_MACHINE(machine); const MemMapEntry *memmap = ibex_memmap; - OpenTitanState *s = g_new0(OpenTitanState, 1); MemoryRegion *sys_mem = get_system_memory(); if (machine->ram_size != mc->default_ram_size) { @@ -330,6 +330,7 @@ static const TypeInfo open_titan_types[] = { }, { .name = TYPE_OPENTITAN_MACHINE, .parent = TYPE_MACHINE, + .instance_size = sizeof(OpenTitanState), .class_init = opentitan_machine_class_init, } }; diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index 806ff73528..609473d07b 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -55,10 +55,11 @@ struct LowRISCIbexSoCState { }; #define TYPE_OPENTITAN_MACHINE MACHINE_TYPE_NAME("opentitan") +OBJECT_DECLARE_SIMPLE_TYPE(OpenTitanState, OPENTITAN_MACHINE) typedef struct OpenTitanState { /*< private >*/ - SysBusDevice parent_obj; + MachineState parent_obj; /*< public >*/ LowRISCIbexSoCState soc; From b9cedbf19cb4be04908a3a623f0f237875483499 Mon Sep 17 00:00:00 2001 From: Yin Wang Date: Fri, 19 May 2023 10:37:58 +0800 Subject: [PATCH 293/412] hw/riscv: qemu crash when NUMA nodes exceed available CPUs Command "qemu-system-riscv64 -machine virt -m 2G -smp 1 -numa node,mem=1G -numa node,mem=1G" would trigger this problem.Backtrace with: #0 0x0000555555b5b1a4 in riscv_numa_get_default_cpu_node_id at ../hw/riscv/numa.c:211 #1 0x00005555558ce510 in machine_numa_finish_cpu_init at ../hw/core/machine.c:1230 #2 0x00005555558ce9d3 in machine_run_board_init at ../hw/core/machine.c:1346 #3 0x0000555555aaedc3 in qemu_init_board at ../softmmu/vl.c:2513 #4 0x0000555555aaf064 in qmp_x_exit_preconfig at ../softmmu/vl.c:2609 #5 0x0000555555ab1916 in qemu_init at ../softmmu/vl.c:3617 #6 0x000055555585463b in main at ../softmmu/main.c:47 This commit fixes the issue by adding parameter checks. Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Reviewed-by: Weiwei Li Signed-off-by: Yin Wang Message-Id: <20230519023758.1759434-1-yin.wang@intel.com> Signed-off-by: Alistair Francis --- hw/riscv/numa.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/riscv/numa.c b/hw/riscv/numa.c index 4720102561..e0414d5b1b 100644 --- a/hw/riscv/numa.c +++ b/hw/riscv/numa.c @@ -207,6 +207,12 @@ int64_t riscv_numa_get_default_cpu_node_id(const MachineState *ms, int idx) { int64_t nidx = 0; + if (ms->numa_state->num_nodes > ms->smp.cpus) { + error_report("Number of NUMA nodes (%d)" + " cannot exceed the number of available CPUs (%d).", + ms->numa_state->num_nodes, ms->smp.max_cpus); + exit(EXIT_FAILURE); + } if (ms->numa_state->num_nodes) { nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); if (ms->numa_state->num_nodes <= nidx) { From 7b945bdc0b10eda16dbeafe6a4dc1480176db38e Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 24 May 2023 09:59:32 +0800 Subject: [PATCH 294/412] target/riscv: Fix pointer mask transformation for vector address actual_address = (requested_address & ~mpmmask) | mpmbase. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Message-Id: <20230524015933.17349-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 8e6c99e573..7505f9470a 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -169,7 +169,7 @@ static inline uint32_t vext_get_total_elems(CPURISCVState *env, uint32_t desc, static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) { - return (addr & env->cur_pmmask) | env->cur_pmbase; + return (addr & ~env->cur_pmmask) | env->cur_pmbase; } /* From 30a0d77622d105908e7d45cf34c73f781263ede5 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Wed, 24 May 2023 09:59:33 +0800 Subject: [PATCH 295/412] target/riscv: Update cur_pmmask/base when xl changes write_mstatus() can only change current xl when in debug mode. And we need update cur_pmmask/base in this case. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: LIU Zhiwei Message-Id: <20230524015933.17349-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index cf7da4f87f..ad73691878 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1324,8 +1324,15 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mstatus = set_field(mstatus, MSTATUS64_SXL, xl); } env->mstatus = mstatus; - env->xl = cpu_recompute_xl(env); + /* + * Except in debug mode, UXL/SXL can only be modified by higher + * privilege mode. So xl will not be changed in normal mode. + */ + if (env->debugger) { + env->xl = cpu_recompute_xl(env); + riscv_cpu_update_mask(env); + } return RISCV_EXCP_NONE; } From 9514fc72d0b92a973297fea0c82d64232a64d127 Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Thu, 18 May 2023 23:20:56 +0530 Subject: [PATCH 296/412] target/riscv: smstateen check for fcsr Implement the s/h/mstateen.fcsr bit as defined in the smstateen spec and check for it when accessing the fcsr register and its fields. Signed-off-by: Mayuresh Chitale Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230518175058.2772506-2-mchitale@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index ad73691878..58499b5afc 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -82,6 +82,10 @@ static RISCVException fs(CPURISCVState *env, int csrno) !riscv_cpu_cfg(env)->ext_zfinx) { return RISCV_EXCP_ILLEGAL_INST; } + + if (!env->debugger && !riscv_cpu_fp_enabled(env)) { + return smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR); + } #endif return RISCV_EXCP_NONE; } @@ -2104,6 +2108,9 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, target_ulong new_val) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } return write_mstateen(env, csrno, wr_mask, new_val); } @@ -2177,6 +2184,10 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + return write_hstateen(env, csrno, wr_mask, new_val); } @@ -2263,6 +2274,10 @@ static RISCVException write_sstateen0(CPURISCVState *env, int csrno, { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + return write_sstateen(env, csrno, wr_mask, new_val); } From e0b343b5fa493f273e0e6d98d632b4360ebd6def Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Thu, 18 May 2023 23:20:57 +0530 Subject: [PATCH 297/412] target/riscv: Reuse tb->flags.FS When misa.F is 0 tb->flags.FS field is unused and can be used to save the current state of smstateen0.FCSR check which is needed by the floating point translation routines. Signed-off-by: Mayuresh Chitale Reviewed-by: Richard Henderson Reviewed-by: Weiwei Li Message-Id: <20230518175058.2772506-3-mchitale@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 6 ++++++ target/riscv/insn_trans/trans_rvf.c.inc | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 35ddd0caac..523311b184 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -120,6 +120,12 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, vs = MIN(vs, get_field(env->mstatus_hs, MSTATUS_VS)); } + /* With Zfinx, floating point is enabled/disabled by Smstateen. */ + if (!riscv_has_ext(env, RVF)) { + fs = (smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR) == RISCV_EXCP_NONE) + ? EXT_STATUS_DIRTY : EXT_STATUS_DISABLED; + } + if (cpu->cfg.debug && !icount_enabled()) { flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER, env->itrigger_enabled); } diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index c47138575a..a0da7391c7 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -19,9 +19,10 @@ */ #define REQUIRE_FPU do {\ - if (ctx->mstatus_fs == EXT_STATUS_DISABLED) \ - if (!ctx->cfg_ptr->ext_zfinx) \ - return false; \ + if (ctx->mstatus_fs == EXT_STATUS_DISABLED) { \ + ctx->virt_inst_excp = ctx->virt_enabled && ctx->cfg_ptr->ext_zfinx; \ + return false; \ + } \ } while (0) #define REQUIRE_ZFINX_OR_F(ctx) do {\ From 3594e3e584d2863e3929a23dd784688942ba167f Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Thu, 18 May 2023 23:20:58 +0530 Subject: [PATCH 298/412] target/riscv: smstateen knobs Add knobs to allow users to enable smstateen and also export it via the ISA extension string. Signed-off-by: Mayuresh Chitale Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-Id: <20230518175058.2772506-4-mchitale@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e67c78f860..d23b4c4d16 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -119,6 +119,7 @@ static const struct isa_ext_data isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), + ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), @@ -1601,8 +1602,8 @@ static Property riscv_cpu_extensions[] = { DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), + DEFINE_PROP_BOOL("smstateen", RISCVCPU, cfg.ext_smstateen, false), DEFINE_PROP_BOOL("svadu", RISCVCPU, cfg.ext_svadu, true), - DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false), DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), DEFINE_PROP_BOOL("svpbmt", RISCVCPU, cfg.ext_svpbmt, false), From d02eb5bcdeb3d3890404c1cc688cbcfcbd3ca65b Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:32 +0800 Subject: [PATCH 299/412] disas: Change type of disassemble_info.target_info to pointer Use pointer to pass more information of target to disasembler, such as pass cpu.cfg related information in following commits. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230523093539.203909-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- include/disas/dis-asm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 2f6f91c2ee..2324f6b1a4 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -397,7 +397,7 @@ typedef struct disassemble_info { char * disassembler_options; /* Field intended to be used by targets in any way they deem suitable. */ - int64_t target_info; + void *target_info; /* Options for Capstone disassembly. */ int cap_arch; From b902ff2946adcc72d65604321ae3ca2fb41ae644 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:33 +0800 Subject: [PATCH 300/412] target/riscv: Split RISCVCPUConfig declarations from cpu.h into cpu_cfg.h Split RISCVCPUConfig declarations to prepare for passing it to disas. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-Id: <20230523093539.203909-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 114 +--------------------------------- target/riscv/cpu_cfg.h | 136 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 113 deletions(-) create mode 100644 target/riscv/cpu_cfg.h diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 1f39edc687..e3e08d315f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -27,6 +27,7 @@ #include "qom/object.h" #include "qemu/int128.h" #include "cpu_bits.h" +#include "cpu_cfg.h" #include "qapi/qapi-types-common.h" #include "cpu-qom.h" @@ -370,119 +371,6 @@ struct CPUArchState { uint64_t kvm_timer_frequency; }; -/* - * map is a 16-bit bitmap: the most significant set bit in map is the maximum - * satp mode that is supported. It may be chosen by the user and must respect - * what qemu implements (valid_1_10_32/64) and what the hw is capable of - * (supported bitmap below). - * - * init is a 16-bit bitmap used to make sure the user selected a correct - * configuration as per the specification. - * - * supported is a 16-bit bitmap used to reflect the hw capabilities. - */ -typedef struct { - uint16_t map, init, supported; -} RISCVSATPMap; - -struct RISCVCPUConfig { - bool ext_zba; - bool ext_zbb; - bool ext_zbc; - bool ext_zbkb; - bool ext_zbkc; - bool ext_zbkx; - bool ext_zbs; - bool ext_zca; - bool ext_zcb; - bool ext_zcd; - bool ext_zce; - bool ext_zcf; - bool ext_zcmp; - bool ext_zcmt; - bool ext_zk; - bool ext_zkn; - bool ext_zknd; - bool ext_zkne; - bool ext_zknh; - bool ext_zkr; - bool ext_zks; - bool ext_zksed; - bool ext_zksh; - bool ext_zkt; - bool ext_ifencei; - bool ext_icsr; - bool ext_icbom; - bool ext_icboz; - bool ext_zicond; - bool ext_zihintpause; - bool ext_smstateen; - bool ext_sstc; - bool ext_svadu; - bool ext_svinval; - bool ext_svnapot; - bool ext_svpbmt; - bool ext_zdinx; - bool ext_zawrs; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zfinx; - bool ext_zhinx; - bool ext_zhinxmin; - bool ext_zve32f; - bool ext_zve64f; - bool ext_zve64d; - bool ext_zmmul; - bool ext_zvfh; - bool ext_zvfhmin; - bool ext_smaia; - bool ext_ssaia; - bool ext_sscofpmf; - bool rvv_ta_all_1s; - bool rvv_ma_all_1s; - - uint32_t mvendorid; - uint64_t marchid; - uint64_t mimpid; - - /* Vendor-specific custom extensions */ - bool ext_xtheadba; - bool ext_xtheadbb; - bool ext_xtheadbs; - bool ext_xtheadcmo; - bool ext_xtheadcondmov; - bool ext_xtheadfmemidx; - bool ext_xtheadfmv; - bool ext_xtheadmac; - bool ext_xtheadmemidx; - bool ext_xtheadmempair; - bool ext_xtheadsync; - bool ext_XVentanaCondOps; - - uint8_t pmu_num; - char *priv_spec; - char *user_spec; - char *bext_spec; - char *vext_spec; - uint16_t vlen; - uint16_t elen; - uint16_t cbom_blocksize; - uint16_t cboz_blocksize; - bool mmu; - bool pmp; - bool epmp; - bool debug; - bool misa_w; - - bool short_isa_string; - -#ifndef CONFIG_USER_ONLY - RISCVSATPMap satp_mode; -#endif -}; - -typedef struct RISCVCPUConfig RISCVCPUConfig; - /* * RISCVCPU: * @env: #CPURISCVState diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h new file mode 100644 index 0000000000..c4a627d335 --- /dev/null +++ b/target/riscv/cpu_cfg.h @@ -0,0 +1,136 @@ +/* + * QEMU RISC-V CPU CFG + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * Copyright (c) 2021-2023 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 RISCV_CPU_CFG_H +#define RISCV_CPU_CFG_H + +/* + * map is a 16-bit bitmap: the most significant set bit in map is the maximum + * satp mode that is supported. It may be chosen by the user and must respect + * what qemu implements (valid_1_10_32/64) and what the hw is capable of + * (supported bitmap below). + * + * init is a 16-bit bitmap used to make sure the user selected a correct + * configuration as per the specification. + * + * supported is a 16-bit bitmap used to reflect the hw capabilities. + */ +typedef struct { + uint16_t map, init, supported; +} RISCVSATPMap; + +struct RISCVCPUConfig { + bool ext_zba; + bool ext_zbb; + bool ext_zbc; + bool ext_zbkb; + bool ext_zbkc; + bool ext_zbkx; + bool ext_zbs; + bool ext_zca; + bool ext_zcb; + bool ext_zcd; + bool ext_zce; + bool ext_zcf; + bool ext_zcmp; + bool ext_zcmt; + bool ext_zk; + bool ext_zkn; + bool ext_zknd; + bool ext_zkne; + bool ext_zknh; + bool ext_zkr; + bool ext_zks; + bool ext_zksed; + bool ext_zksh; + bool ext_zkt; + bool ext_ifencei; + bool ext_icsr; + bool ext_icbom; + bool ext_icboz; + bool ext_zicond; + bool ext_zihintpause; + bool ext_smstateen; + bool ext_sstc; + bool ext_svadu; + bool ext_svinval; + bool ext_svnapot; + bool ext_svpbmt; + bool ext_zdinx; + bool ext_zawrs; + bool ext_zfh; + bool ext_zfhmin; + bool ext_zfinx; + bool ext_zhinx; + bool ext_zhinxmin; + bool ext_zve32f; + bool ext_zve64f; + bool ext_zve64d; + bool ext_zmmul; + bool ext_zvfh; + bool ext_zvfhmin; + bool ext_smaia; + bool ext_ssaia; + bool ext_sscofpmf; + bool rvv_ta_all_1s; + bool rvv_ma_all_1s; + + uint32_t mvendorid; + uint64_t marchid; + uint64_t mimpid; + + /* Vendor-specific custom extensions */ + bool ext_xtheadba; + bool ext_xtheadbb; + bool ext_xtheadbs; + bool ext_xtheadcmo; + bool ext_xtheadcondmov; + bool ext_xtheadfmemidx; + bool ext_xtheadfmv; + bool ext_xtheadmac; + bool ext_xtheadmemidx; + bool ext_xtheadmempair; + bool ext_xtheadsync; + bool ext_XVentanaCondOps; + + uint8_t pmu_num; + char *priv_spec; + char *user_spec; + char *bext_spec; + char *vext_spec; + uint16_t vlen; + uint16_t elen; + uint16_t cbom_blocksize; + uint16_t cboz_blocksize; + bool mmu; + bool pmp; + bool epmp; + bool debug; + bool misa_w; + + bool short_isa_string; + +#ifndef CONFIG_USER_ONLY + RISCVSATPMap satp_mode; +#endif +}; + +typedef struct RISCVCPUConfig RISCVCPUConfig; +#endif From 454c2201005b5f47a76116ab529c923e194ec615 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:34 +0800 Subject: [PATCH 301/412] target/riscv: Pass RISCVCPUConfig as target_info to disassemble_info Pass RISCVCPUConfig as disassemble_info.target_info to support disas of conflict instructions related to specific extensions. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230523093539.203909-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 10 +++++++--- target/riscv/cpu.c | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index d597161d46..f2dd5fd531 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "disas/dis-asm.h" - +#include "target/riscv/cpu_cfg.h" /* types */ @@ -969,6 +969,7 @@ typedef enum { /* structures */ typedef struct { + RISCVCPUConfig *cfg; uint64_t pc; uint64_t inst; int32_t imm; @@ -4861,11 +4862,13 @@ static void decode_inst_decompress(rv_decode *dec, rv_isa isa) /* disassemble instruction */ static void -disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) +disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, + RISCVCPUConfig *cfg) { rv_decode dec = { 0 }; dec.pc = pc; dec.inst = inst; + dec.cfg = cfg; decode_inst_opcode(&dec, isa); decode_inst_operands(&dec, isa); decode_inst_decompress(&dec, isa); @@ -4920,7 +4923,8 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) break; } - disasm_inst(buf, sizeof(buf), isa, memaddr, inst); + disasm_inst(buf, sizeof(buf), isa, memaddr, inst, + (RISCVCPUConfig *)info->target_info); (*info->fprintf_func)(info->stream, "%s", buf); return len; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d23b4c4d16..938c7bd87b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -849,6 +849,7 @@ static void riscv_cpu_reset_hold(Object *obj) static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) { RISCVCPU *cpu = RISCV_CPU(s); + info->target_info = &cpu->cfg; switch (riscv_cpu_mxl(&cpu->env)) { case MXL_RV32: From 2a2b221b65f5a4bcbbb9b508cdb76ff527e48aa6 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:35 +0800 Subject: [PATCH 302/412] disas/riscv.c: Support disas for Zcm* extensions Support disas for Zcmt* instructions only when related extensions are supported. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230523093539.203909-5-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index f2dd5fd531..6659f92179 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2505,7 +2505,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) op = rv_op_c_sqsp; } else { op = rv_op_c_fsdsp; - if (((inst >> 12) & 0b01)) { + if (dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { switch ((inst >> 8) & 0b01111) { case 8: if (((inst >> 4) & 0b01111) >= 4) { @@ -2531,6 +2531,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } else { switch ((inst >> 10) & 0b011) { case 0: + if (!dec->cfg->ext_zcmt) { + break; + } if (((inst >> 2) & 0xFF) >= 32) { op = rv_op_cm_jalt; } else { @@ -2538,6 +2541,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: + if (!dec->cfg->ext_zcmp) { + break; + } switch ((inst >> 5) & 0b011) { case 1: op = rv_op_cm_mvsa01; break; case 3: op = rv_op_cm_mva01s; break; From c54dab4c85f2ce67c6566f3b01a1e09ebfab8c57 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:36 +0800 Subject: [PATCH 303/412] disas/riscv.c: Support disas for Z*inx extensions Support disas for Z*inx instructions only when Zfinx extension is supported. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230523093539.203909-6-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 6659f92179..c9a81af662 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4598,16 +4598,24 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) append(buf, rv_ireg_name_sym[dec->rs2], buflen); break; case '3': - append(buf, rv_freg_name_sym[dec->rd], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rd] : + rv_freg_name_sym[dec->rd], + buflen); break; case '4': - append(buf, rv_freg_name_sym[dec->rs1], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs1] : + rv_freg_name_sym[dec->rs1], + buflen); break; case '5': - append(buf, rv_freg_name_sym[dec->rs2], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs2] : + rv_freg_name_sym[dec->rs2], + buflen); break; case '6': - append(buf, rv_freg_name_sym[dec->rs3], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs3] : + rv_freg_name_sym[dec->rs3], + buflen); break; case '7': snprintf(tmp, sizeof(tmp), "%d", dec->rs1); From 8deb4756a9d9ed271d05b2eeffa0e0e1483669f3 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:37 +0800 Subject: [PATCH 304/412] disas/riscv.c: Remove unused decomp_rv32/64 value for vector instructions Currently decomp_rv32 and decomp_rv64 value in opcode_data for vector instructions are the same op index as their own. And they have no functional decomp_data. So they have no functional difference from just leaving them as zero. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-Id: <20230523093539.203909-7-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 740 +++++++++++++++++++++++++------------------------- 1 file changed, 370 insertions(+), 370 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index c9a81af662..806c4b6f93 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1732,376 +1732,376 @@ const rv_opcode_data opcode_data[] = { { "zip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "xperm8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle8_v, rv_op_vle8_v, 0 }, - { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle16_v, rv_op_vle16_v, 0 }, - { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle32_v, rv_op_vle32_v, 0 }, - { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle64_v, rv_op_vle64_v, 0 }, - { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse8_v, rv_op_vse8_v, 0 }, - { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse16_v, rv_op_vse16_v, 0 }, - { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse32_v, rv_op_vse32_v, 0 }, - { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse64_v, rv_op_vse64_v, 0 }, - { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vlm_v, rv_op_vlm_v, 0 }, - { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vsm_v, rv_op_vsm_v, 0 }, - { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse8_v, rv_op_vlse8_v, 0 }, - { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse16_v, rv_op_vlse16_v, 0 }, - { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse32_v, rv_op_vlse32_v, 0 }, - { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse64_v, rv_op_vlse64_v, 0 }, - { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse8_v, rv_op_vsse8_v, 0 }, - { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse16_v, rv_op_vsse16_v, 0 }, - { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse32_v, rv_op_vsse32_v, 0 }, - { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse64_v, rv_op_vsse64_v, 0 }, - { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei8_v, rv_op_vluxei8_v, 0 }, - { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei16_v, rv_op_vluxei16_v, 0 }, - { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei32_v, rv_op_vluxei32_v, 0 }, - { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei64_v, rv_op_vluxei64_v, 0 }, - { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei8_v, rv_op_vloxei8_v, 0 }, - { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei16_v, rv_op_vloxei16_v, 0 }, - { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei32_v, rv_op_vloxei32_v, 0 }, - { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei64_v, rv_op_vloxei64_v, 0 }, - { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei8_v, rv_op_vsuxei8_v, 0 }, - { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei16_v, rv_op_vsuxei16_v, 0 }, - { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei32_v, rv_op_vsuxei32_v, 0 }, - { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei64_v, rv_op_vsuxei64_v, 0 }, - { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei8_v, rv_op_vsoxei8_v, 0 }, - { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei16_v, rv_op_vsoxei16_v, 0 }, - { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei32_v, rv_op_vsoxei32_v, 0 }, - { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei64_v, rv_op_vsoxei64_v, 0 }, - { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle8ff_v, rv_op_vle8ff_v, 0 }, - { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle16ff_v, rv_op_vle16ff_v, 0 }, - { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle32ff_v, rv_op_vle32ff_v, 0 }, - { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle64ff_v, rv_op_vle64ff_v, 0 }, - { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re8_v, rv_op_vl1re8_v, 0 }, - { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re16_v, rv_op_vl1re16_v, 0 }, - { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re32_v, rv_op_vl1re32_v, 0 }, - { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re64_v, rv_op_vl1re64_v, 0 }, - { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re8_v, rv_op_vl2re8_v, 0 }, - { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re16_v, rv_op_vl2re16_v, 0 }, - { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re32_v, rv_op_vl2re32_v, 0 }, - { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re64_v, rv_op_vl2re64_v, 0 }, - { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re8_v, rv_op_vl4re8_v, 0 }, - { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re16_v, rv_op_vl4re16_v, 0 }, - { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re32_v, rv_op_vl4re32_v, 0 }, - { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re64_v, rv_op_vl4re64_v, 0 }, - { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re8_v, rv_op_vl8re8_v, 0 }, - { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re16_v, rv_op_vl8re16_v, 0 }, - { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re32_v, rv_op_vl8re32_v, 0 }, - { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re64_v, rv_op_vl8re64_v, 0 }, - { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs1r_v, rv_op_vs1r_v, 0 }, - { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs2r_v, rv_op_vs2r_v, 0 }, - { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs4r_v, rv_op_vs4r_v, 0 }, - { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs8r_v, rv_op_vs8r_v, 0 }, - { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vadd_vv, rv_op_vadd_vv, 0 }, - { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vadd_vx, rv_op_vadd_vx, 0 }, - { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vadd_vi, rv_op_vadd_vi, 0 }, - { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsub_vv, rv_op_vsub_vv, 0 }, - { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsub_vx, rv_op_vsub_vx, 0 }, - { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vrsub_vx, rv_op_vrsub_vx, 0 }, - { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vrsub_vi, rv_op_vrsub_vi, 0 }, - { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwaddu_vv, rv_op_vwaddu_vv, 0 }, - { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwaddu_vx, rv_op_vwaddu_vx, 0 }, - { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwadd_vv, rv_op_vwadd_vv, 0 }, - { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwadd_vx, rv_op_vwadd_vx, 0 }, - { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsubu_vv, rv_op_vwsubu_vv, 0 }, - { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsubu_vx, rv_op_vwsubu_vx, 0 }, - { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsub_vv, rv_op_vwsub_vv, 0 }, - { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsub_vx, rv_op_vwsub_vx, 0 }, - { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwaddu_wv, rv_op_vwaddu_wv, 0 }, - { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwaddu_wx, rv_op_vwaddu_wx, 0 }, - { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwadd_wv, rv_op_vwadd_wv, 0 }, - { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwadd_wx, rv_op_vwadd_wx, 0 }, - { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsubu_wv, rv_op_vwsubu_wv, 0 }, - { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsubu_wx, rv_op_vwsubu_wx, 0 }, - { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsub_wv, rv_op_vwsub_wv, 0 }, - { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsub_wx, rv_op_vwsub_wx, 0 }, - { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vadc_vvm, rv_op_vadc_vvm, 0 }, - { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vadc_vxm, rv_op_vadc_vxm, 0 }, - { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, rv_op_vadc_vim, rv_op_vadc_vim, 0 }, - { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vmadc_vvm, rv_op_vmadc_vvm, 0 }, - { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vmadc_vxm, rv_op_vmadc_vxm, 0 }, - { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, rv_op_vmadc_vim, rv_op_vmadc_vim, 0 }, - { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vsbc_vvm, rv_op_vsbc_vvm, 0 }, - { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vsbc_vxm, rv_op_vsbc_vxm, 0 }, - { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vmsbc_vvm, rv_op_vmsbc_vvm, 0 }, - { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vmsbc_vxm, rv_op_vmsbc_vxm, 0 }, - { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vand_vv, rv_op_vand_vv, 0 }, - { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vand_vx, rv_op_vand_vx, 0 }, - { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vand_vi, rv_op_vand_vi, 0 }, - { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vor_vv, rv_op_vor_vv, 0 }, - { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vor_vx, rv_op_vor_vx, 0 }, - { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vor_vi, rv_op_vor_vi, 0 }, - { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vxor_vv, rv_op_vxor_vv, 0 }, - { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vxor_vx, rv_op_vxor_vx, 0 }, - { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vxor_vi, rv_op_vxor_vi, 0 }, - { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsll_vv, rv_op_vsll_vv, 0 }, - { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsll_vx, rv_op_vsll_vx, 0 }, - { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vsll_vi, rv_op_vsll_vi, 0 }, - { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsrl_vv, rv_op_vsrl_vv, 0 }, - { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsrl_vx, rv_op_vsrl_vx, 0 }, - { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vsrl_vi, rv_op_vsrl_vi, 0 }, - { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsra_vv, rv_op_vsra_vv, 0 }, - { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsra_vx, rv_op_vsra_vx, 0 }, - { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vsra_vi, rv_op_vsra_vi, 0 }, - { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnsrl_wv, rv_op_vnsrl_wv, 0 }, - { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnsrl_wx, rv_op_vnsrl_wx, 0 }, - { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnsrl_wi, rv_op_vnsrl_wi, 0 }, - { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnsra_wv, rv_op_vnsra_wv, 0 }, - { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnsra_wx, rv_op_vnsra_wx, 0 }, - { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnsra_wi, rv_op_vnsra_wi, 0 }, - { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmseq_vv, rv_op_vmseq_vv, 0 }, - { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmseq_vx, rv_op_vmseq_vx, 0 }, - { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmseq_vi, rv_op_vmseq_vi, 0 }, - { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsne_vv, rv_op_vmsne_vv, 0 }, - { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsne_vx, rv_op_vmsne_vx, 0 }, - { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsne_vi, rv_op_vmsne_vi, 0 }, - { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsltu_vv, rv_op_vmsltu_vv, 0 }, - { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsltu_vx, rv_op_vmsltu_vx, 0 }, - { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmslt_vv, rv_op_vmslt_vv, 0 }, - { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmslt_vx, rv_op_vmslt_vx, 0 }, - { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsleu_vv, rv_op_vmsleu_vv, 0 }, - { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsleu_vx, rv_op_vmsleu_vx, 0 }, - { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsleu_vi, rv_op_vmsleu_vi, 0 }, - { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsle_vv, rv_op_vmsle_vv, 0 }, - { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsle_vx, rv_op_vmsle_vx, 0 }, - { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsle_vi, rv_op_vmsle_vi, 0 }, - { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsgtu_vx, rv_op_vmsgtu_vx, 0 }, - { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsgtu_vi, rv_op_vmsgtu_vi, 0 }, - { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsgt_vx, rv_op_vmsgt_vx, 0 }, - { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsgt_vi, rv_op_vmsgt_vi, 0 }, - { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vminu_vv, rv_op_vminu_vv, 0 }, - { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vminu_vx, rv_op_vminu_vx, 0 }, - { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmin_vv, rv_op_vmin_vv, 0 }, - { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmin_vx, rv_op_vmin_vx, 0 }, - { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmaxu_vv, rv_op_vmaxu_vv, 0 }, - { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmaxu_vx, rv_op_vmaxu_vx, 0 }, - { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmax_vv, rv_op_vmax_vv, 0 }, - { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmax_vx, rv_op_vmax_vx, 0 }, - { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmul_vv, rv_op_vmul_vv, 0 }, - { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmul_vx, rv_op_vmul_vx, 0 }, - { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmulh_vv, rv_op_vmulh_vv, 0 }, - { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmulh_vx, rv_op_vmulh_vx, 0 }, - { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmulhu_vv, rv_op_vmulhu_vv, 0 }, - { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmulhu_vx, rv_op_vmulhu_vx, 0 }, - { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmulhsu_vv, rv_op_vmulhsu_vv, 0 }, - { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmulhsu_vx, rv_op_vmulhsu_vx, 0 }, - { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vdivu_vv, rv_op_vdivu_vv, 0 }, - { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vdivu_vx, rv_op_vdivu_vx, 0 }, - { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vdiv_vv, rv_op_vdiv_vv, 0 }, - { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vdiv_vx, rv_op_vdiv_vx, 0 }, - { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vremu_vv, rv_op_vremu_vv, 0 }, - { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vremu_vx, rv_op_vremu_vx, 0 }, - { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vrem_vv, rv_op_vrem_vv, 0 }, - { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vrem_vx, rv_op_vrem_vx, 0 }, - { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwmulu_vv, rv_op_vwmulu_vv, 0 }, - { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwmulu_vx, rv_op_vwmulu_vx, 0 }, - { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwmulsu_vv, rv_op_vwmulsu_vv, 0 }, - { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwmulsu_vx, rv_op_vwmulsu_vx, 0 }, - { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwmul_vv, rv_op_vwmul_vv, 0 }, - { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwmul_vx, rv_op_vwmul_vx, 0 }, - { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vmacc_vv, rv_op_vmacc_vv, 0 }, - { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vmacc_vx, rv_op_vmacc_vx, 0 }, - { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vnmsac_vv, rv_op_vnmsac_vv, 0 }, - { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vnmsac_vx, rv_op_vnmsac_vx, 0 }, - { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vmadd_vv, rv_op_vmadd_vv, 0 }, - { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vmadd_vx, rv_op_vmadd_vx, 0 }, - { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vnmsub_vv, rv_op_vnmsub_vv, 0 }, - { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vnmsub_vx, rv_op_vnmsub_vx, 0 }, - { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vwmaccu_vv, rv_op_vwmaccu_vv, 0 }, - { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmaccu_vx, rv_op_vwmaccu_vx, 0 }, - { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vwmacc_vv, rv_op_vwmacc_vv, 0 }, - { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmacc_vx, rv_op_vwmacc_vx, 0 }, - { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vwmaccsu_vv, rv_op_vwmaccsu_vv, 0 }, - { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmaccsu_vx, rv_op_vwmaccsu_vx, 0 }, - { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmaccus_vx, rv_op_vwmaccus_vx, 0 }, - { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1, NULL, rv_op_vmv_v_v, rv_op_vmv_v_v, 0 }, - { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, rv_op_vmv_v_x, rv_op_vmv_v_x, 0 }, - { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm, NULL, rv_op_vmv_v_i, rv_op_vmv_v_i, 0 }, - { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vmerge_vvm, rv_op_vmerge_vvm, 0 }, - { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vmerge_vxm, rv_op_vmerge_vxm, 0 }, - { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, rv_op_vmerge_vim, rv_op_vmerge_vim, 0 }, - { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsaddu_vv, rv_op_vsaddu_vv, 0 }, - { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsaddu_vx, rv_op_vsaddu_vx, 0 }, - { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vsaddu_vi, rv_op_vsaddu_vi, 0 }, - { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsadd_vv, rv_op_vsadd_vv, 0 }, - { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsadd_vx, rv_op_vsadd_vx, 0 }, - { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vsadd_vi, rv_op_vsadd_vi, 0 }, - { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssubu_vv, rv_op_vssubu_vv, 0 }, - { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssubu_vx, rv_op_vssubu_vx, 0 }, - { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssub_vv, rv_op_vssub_vv, 0 }, - { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssub_vx, rv_op_vssub_vx, 0 }, - { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vaadd_vv, rv_op_vaadd_vv, 0 }, - { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vaadd_vx, rv_op_vaadd_vx, 0 }, - { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vaaddu_vv, rv_op_vaaddu_vv, 0 }, - { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vaaddu_vx, rv_op_vaaddu_vx, 0 }, - { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vasub_vv, rv_op_vasub_vv, 0 }, - { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vasub_vx, rv_op_vasub_vx, 0 }, - { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vasubu_vv, rv_op_vasubu_vv, 0 }, - { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vasubu_vx, rv_op_vasubu_vx, 0 }, - { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsmul_vv, rv_op_vsmul_vv, 0 }, - { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsmul_vx, rv_op_vsmul_vx, 0 }, - { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssrl_vv, rv_op_vssrl_vv, 0 }, - { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssrl_vx, rv_op_vssrl_vx, 0 }, - { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vssrl_vi, rv_op_vssrl_vi, 0 }, - { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssra_vv, rv_op_vssra_vv, 0 }, - { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssra_vx, rv_op_vssra_vx, 0 }, - { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vssra_vi, rv_op_vssra_vi, 0 }, - { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnclipu_wv, rv_op_vnclipu_wv, 0 }, - { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnclipu_wx, rv_op_vnclipu_wx, 0 }, - { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnclipu_wi, rv_op_vnclipu_wi, 0 }, - { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnclip_wv, rv_op_vnclip_wv, 0 }, - { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnclip_wx, rv_op_vnclip_wx, 0 }, - { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnclip_wi, rv_op_vnclip_wi, 0 }, - { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfadd_vv, rv_op_vfadd_vv, 0 }, - { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfadd_vf, rv_op_vfadd_vf, 0 }, - { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsub_vv, rv_op_vfsub_vv, 0 }, - { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsub_vf, rv_op_vfsub_vf, 0 }, - { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfrsub_vf, rv_op_vfrsub_vf, 0 }, - { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwadd_vv, rv_op_vfwadd_vv, 0 }, - { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwadd_vf, rv_op_vfwadd_vf, 0 }, - { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwadd_wv, rv_op_vfwadd_wv, 0 }, - { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwadd_wf, rv_op_vfwadd_wf, 0 }, - { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwsub_vv, rv_op_vfwsub_vv, 0 }, - { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwsub_vf, rv_op_vfwsub_vf, 0 }, - { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwsub_wv, rv_op_vfwsub_wv, 0 }, - { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwsub_wf, rv_op_vfwsub_wf, 0 }, - { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfmul_vv, rv_op_vfmul_vv, 0 }, - { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfmul_vf, rv_op_vfmul_vf, 0 }, - { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfdiv_vv, rv_op_vfdiv_vv, 0 }, - { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfdiv_vf, rv_op_vfdiv_vf, 0 }, - { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfrdiv_vf, rv_op_vfrdiv_vf, 0 }, - { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwmul_vv, rv_op_vfwmul_vv, 0 }, - { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwmul_vf, rv_op_vfwmul_vf, 0 }, - { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmacc_vv, rv_op_vfmacc_vv, 0 }, - { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmacc_vf, rv_op_vfmacc_vf, 0 }, - { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmacc_vv, rv_op_vfnmacc_vv, 0 }, - { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmacc_vf, rv_op_vfnmacc_vf, 0 }, - { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmsac_vv, rv_op_vfmsac_vv, 0 }, - { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmsac_vf, rv_op_vfmsac_vf, 0 }, - { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmsac_vv, rv_op_vfnmsac_vv, 0 }, - { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmsac_vf, rv_op_vfnmsac_vf, 0 }, - { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmadd_vv, rv_op_vfmadd_vv, 0 }, - { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmadd_vf, rv_op_vfmadd_vf, 0 }, - { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmadd_vv, rv_op_vfnmadd_vv, 0 }, - { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmadd_vf, rv_op_vfnmadd_vf, 0 }, - { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmsub_vv, rv_op_vfmsub_vv, 0 }, - { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmsub_vf, rv_op_vfmsub_vf, 0 }, - { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmsub_vv, rv_op_vfnmsub_vv, 0 }, - { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmsub_vf, rv_op_vfnmsub_vf, 0 }, - { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwmacc_vv, rv_op_vfwmacc_vv, 0 }, - { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwmacc_vf, rv_op_vfwmacc_vf, 0 }, - { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwnmacc_vv, rv_op_vfwnmacc_vv, 0 }, - { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwnmacc_vf, rv_op_vfwnmacc_vf, 0 }, - { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwmsac_vv, rv_op_vfwmsac_vv, 0 }, - { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwmsac_vf, rv_op_vfwmsac_vf, 0 }, - { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwnmsac_vv, rv_op_vfwnmsac_vv, 0 }, - { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwnmsac_vf, rv_op_vfwnmsac_vf, 0 }, - { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vfsqrt_v, rv_op_vfsqrt_v, 0 }, - { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vfrsqrt7_v, rv_op_vfrsqrt7_v, 0 }, - { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vfrec7_v, rv_op_vfrec7_v, 0 }, - { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfmin_vv, rv_op_vfmin_vv, 0 }, - { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfmin_vf, rv_op_vfmin_vf, 0 }, - { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfmax_vv, rv_op_vfmax_vv, 0 }, - { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfmax_vf, rv_op_vfmax_vf, 0 }, - { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsgnj_vv, rv_op_vfsgnj_vv, 0 }, - { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsgnj_vf, rv_op_vfsgnj_vf, 0 }, - { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsgnjn_vv, rv_op_vfsgnjn_vv, 0 }, - { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsgnjn_vf, rv_op_vfsgnjn_vf, 0 }, - { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsgnjx_vv, rv_op_vfsgnjx_vv, 0 }, - { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsgnjx_vf, rv_op_vfsgnjx_vf, 0 }, - { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfslide1up_vf, rv_op_vfslide1up_vf, 0 }, - { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfslide1down_vf, rv_op_vfslide1down_vf, 0 }, - { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmfeq_vv, rv_op_vmfeq_vv, 0 }, - { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfeq_vf, rv_op_vmfeq_vf, 0 }, - { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmfne_vv, rv_op_vmfne_vv, 0 }, - { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfne_vf, rv_op_vmfne_vf, 0 }, - { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmflt_vv, rv_op_vmflt_vv, 0 }, - { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmflt_vf, rv_op_vmflt_vf, 0 }, - { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmfle_vv, rv_op_vmfle_vv, 0 }, - { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfle_vf, rv_op_vmfle_vf, 0 }, - { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfgt_vf, rv_op_vmfgt_vf, 0 }, - { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfge_vf, rv_op_vmfge_vf, 0 }, - { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfclass_v, rv_op_vfclass_v, 0 }, - { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl, NULL, rv_op_vfmerge_vfm, rv_op_vfmerge_vfm, 0 }, - { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, rv_op_vfmv_v_f, rv_op_vfmv_v_f, 0 }, - { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_xu_f_v, rv_op_vfcvt_xu_f_v, 0 }, - { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_x_f_v, rv_op_vfcvt_x_f_v, 0 }, - { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_f_xu_v, rv_op_vfcvt_f_xu_v, 0 }, - { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_f_x_v, rv_op_vfcvt_f_x_v, 0 }, - { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_rtz_xu_f_v, rv_op_vfcvt_rtz_xu_f_v, 0 }, - { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_rtz_x_f_v, rv_op_vfcvt_rtz_x_f_v, 0 }, - { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_xu_f_v, rv_op_vfwcvt_xu_f_v, 0 }, - { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_x_f_v, rv_op_vfwcvt_x_f_v, 0 }, - { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_f_xu_v, rv_op_vfwcvt_f_xu_v, 0 }, - { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_f_x_v, rv_op_vfwcvt_f_x_v, 0 }, - { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_f_f_v, rv_op_vfwcvt_f_f_v, 0 }, - { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_rtz_xu_f_v, rv_op_vfwcvt_rtz_xu_f_v, 0 }, - { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_rtz_x_f_v, rv_op_vfwcvt_rtz_x_f_v, 0 }, - { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_xu_f_w, rv_op_vfncvt_xu_f_w, 0 }, - { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_x_f_w, rv_op_vfncvt_x_f_w, 0 }, - { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_f_xu_w, rv_op_vfncvt_f_xu_w, 0 }, - { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_f_x_w, rv_op_vfncvt_f_x_w, 0 }, - { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_f_f_w, rv_op_vfncvt_f_f_w, 0 }, - { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_rod_f_f_w, rv_op_vfncvt_rod_f_f_w, 0 }, - { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_rtz_xu_f_w, rv_op_vfncvt_rtz_xu_f_w, 0 }, - { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_rtz_x_f_w, rv_op_vfncvt_rtz_x_f_w, 0 }, - { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredsum_vs, rv_op_vredsum_vs, 0 }, - { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredand_vs, rv_op_vredand_vs, 0 }, - { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredor_vs, rv_op_vredor_vs, 0 }, - { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredxor_vs, rv_op_vredxor_vs, 0 }, - { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredminu_vs, rv_op_vredminu_vs, 0 }, - { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredmin_vs, rv_op_vredmin_vs, 0 }, - { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredmaxu_vs, rv_op_vredmaxu_vs, 0 }, - { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredmax_vs, rv_op_vredmax_vs, 0 }, - { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwredsumu_vs, rv_op_vwredsumu_vs, 0 }, - { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwredsum_vs, rv_op_vwredsum_vs, 0 }, - { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredusum_vs, rv_op_vfredusum_vs, 0 }, - { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredosum_vs, rv_op_vfredosum_vs, 0 }, - { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredmin_vs, rv_op_vfredmin_vs, 0 }, - { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredmax_vs, rv_op_vfredmax_vs, 0 }, - { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwredusum_vs, rv_op_vfwredusum_vs, 0 }, - { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwredosum_vs, rv_op_vfwredosum_vs, 0 }, - { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmand_mm, rv_op_vmand_mm, 0 }, - { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmnand_mm, rv_op_vmnand_mm, 0 }, - { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmandn_mm, rv_op_vmandn_mm, 0 }, - { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmxor_mm, rv_op_vmxor_mm, 0 }, - { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmor_mm, rv_op_vmor_mm, 0 }, - { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmnor_mm, rv_op_vmnor_mm, 0 }, - { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmorn_mm, rv_op_vmorn_mm, 0 }, - { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmxnor_mm, rv_op_vmxnor_mm, 0 }, - { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, rv_op_vcpop_m, rv_op_vcpop_m, 0 }, - { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, rv_op_vfirst_m, rv_op_vfirst_m, 0 }, - { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vmsbf_m, rv_op_vmsbf_m, 0 }, - { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vmsif_m, rv_op_vmsif_m, 0 }, - { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vmsof_m, rv_op_vmsof_m, 0 }, - { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_viota_m, rv_op_viota_m, 0 }, - { "vid.v", rv_codec_v_r, rv_fmt_vd_vm, NULL, rv_op_vid_v, rv_op_vid_v, 0 }, - { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2, NULL, rv_op_vmv_x_s, rv_op_vmv_x_s, 0 }, - { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, rv_op_vmv_s_x, rv_op_vmv_s_x, 0 }, - { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, rv_op_vfmv_f_s, rv_op_vfmv_f_s, 0 }, - { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, rv_op_vfmv_s_f, rv_op_vfmv_s_f, 0 }, - { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslideup_vx, rv_op_vslideup_vx, 0 }, - { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vslideup_vi, rv_op_vslideup_vi, 0 }, - { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslide1up_vx, rv_op_vslide1up_vx, 0 }, - { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslidedown_vx, rv_op_vslidedown_vx, 0 }, - { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vslidedown_vi, rv_op_vslidedown_vi, 0 }, - { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslide1down_vx, rv_op_vslide1down_vx, 0 }, - { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vrgather_vv, rv_op_vrgather_vv, 0 }, - { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vrgatherei16_vv, rv_op_vrgatherei16_vv, 0 }, - { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vrgather_vx, rv_op_vrgather_vx, 0 }, - { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vrgather_vi, rv_op_vrgather_vi, 0 }, - { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, rv_op_vcompress_vm, rv_op_vcompress_vm, 0 }, - { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv1r_v, rv_op_vmv1r_v, 0 }, - { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv2r_v, rv_op_vmv2r_v, 0 }, - { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv4r_v, rv_op_vmv4r_v, 0 }, - { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv8r_v, rv_op_vmv8r_v, 0 }, - { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vzext_vf2, rv_op_vzext_vf2, 0 }, - { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vzext_vf4, rv_op_vzext_vf4, 0 }, - { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vzext_vf8, rv_op_vzext_vf8, 0 }, - { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf2, rv_op_vsext_vf2, 0 }, - { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf4, rv_op_vsext_vf4, 0 }, - { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf8, rv_op_vsext_vf8, 0 }, - { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, rv_op_vsetvli, rv_op_vsetvli, 0 }, - { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, rv_op_vsetivli, rv_op_vsetivli, 0 }, - { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, rv_op_vsetvl, rv_op_vsetvl, 0 }, + { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, + { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, + { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1, NULL, 0, 0, 0 }, + { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, + { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm, NULL, 0, 0, 0 }, + { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, + { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl, NULL, 0, 0, 0 }, + { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, + { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, + { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, + { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vid.v", rv_codec_v_r, rv_fmt_vd_vm, NULL, 0, 0, 0 }, + { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2, NULL, 0, 0, 0 }, + { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, + { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, 0, 0, 0 }, + { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, + { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, 0, 0, 0 }, + { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, 0, 0, 0 }, + { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, { "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, { "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, From 98624d137321c5cda021437c0f02cdf40ee7f752 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:38 +0800 Subject: [PATCH 305/412] disas/riscv.c: Fix lines with over 80 characters Fix lines with over 80 characters. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-Id: <20230523093539.203909-8-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 201 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 61 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 806c4b6f93..bc433c4120 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1110,8 +1110,10 @@ static const char rv_vreg_name_sym[32][4] = { /* pseudo-instruction constraints */ static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; -static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, + rvc_end }; +static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, + rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end }; static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end }; @@ -1141,18 +1143,28 @@ static const rvc_constraint rvcc_bleu[] = { rvc_end }; static const rvc_constraint rvcc_bgt[] = { rvc_end }; static const rvc_constraint rvcc_bgtu[] = { rvc_end }; static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; -static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; -static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, rvc_end }; -static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, rvc_end }; -static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end }; -static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; -static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end }; +static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, + rvc_end }; +static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, + rvc_end }; +static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, + rvc_end }; +static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, + rvc_end }; +static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, + rvc_csr_eq_0xc02, rvc_end }; +static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, + rvc_csr_eq_0xc80, rvc_end }; +static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, + rvc_end }; static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc82, rvc_end }; -static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end }; -static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end }; -static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end }; +static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, + rvc_end }; +static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, + rvc_end }; +static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, + rvc_end }; static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end }; static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end }; @@ -1554,17 +1566,23 @@ const rv_opcode_data opcode_data[] = { { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, - { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 }, - { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, + { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, + rv_op_fld, 0 }, + { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, + rv_op_lw }, { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, - { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, 0 }, - { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, + { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, + rv_op_fsd, 0 }, + { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, + rv_op_sw }, { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, - { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, + rv_op_addi }, { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, - { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, + rv_op_addi }, { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, @@ -1575,37 +1593,63 @@ const rv_opcode_data opcode_data[] = { rv_op_srai, rv_op_srai, rvcd_imm_nz }, { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi }, - { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub }, - { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor }, - { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or }, - { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, rv_op_and }, - { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, rv_op_subw }, - { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, rv_op_addw }, - { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal }, - { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq }, - { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne }, + { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, + rv_op_sub }, + { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, + rv_op_xor }, + { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, + rv_op_or }, + { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, + rv_op_and }, + { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, + rv_op_subw }, + { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, + rv_op_addw }, + { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, + rv_op_jal }, + { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, + rv_op_beq }, + { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, + rv_op_bne }, { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli, rvcd_imm_nz }, - { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld }, - { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, - { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, - { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr }, - { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, rv_op_ebreak, rv_op_ebreak }, - { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr }, - { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, rv_op_add }, - { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, rv_op_fsd }, - { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, - { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, - { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld }, - { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd }, - { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, rv_op_addiw }, - { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld }, - { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd }, + { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, + rv_op_fld, rv_op_fld }, + { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, + rv_op_lw, rv_op_lw }, + { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, + 0 }, + { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, + rv_op_jalr, rv_op_jalr }, + { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, + rv_op_addi }, + { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, + rv_op_ebreak, rv_op_ebreak }, + { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, + rv_op_jalr, rv_op_jalr }, + { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, + rv_op_add }, + { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, + rv_op_fsd, rv_op_fsd }, + { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, + rv_op_sw, rv_op_sw }, + { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, + 0 }, + { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, + rv_op_ld }, + { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, + rv_op_sd }, + { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, + rv_op_addiw }, + { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, + rv_op_ld }, + { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, + rv_op_sd }, { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, - { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, + { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, + rv_op_sq }, { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, @@ -2844,7 +2888,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 11: - switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 24) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 2: op = rv_op_amoadd_w; break; case 3: op = rv_op_amoadd_d; break; case 4: op = rv_op_amoadd_q; break; @@ -2893,7 +2938,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 12: - switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + switch (((inst >> 22) & 0b1111111000) | + ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_add; break; case 1: op = rv_op_sll; break; case 2: op = rv_op_slt; break; @@ -2966,7 +3012,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 13: op = rv_op_lui; break; case 14: - switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + switch (((inst >> 22) & 0b1111111000) | + ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_addw; break; case 1: op = rv_op_sllw; break; case 5: op = rv_op_srlw; break; @@ -3175,35 +3222,41 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 112: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_s; break; case 1: op = rv_op_fclass_s; break; } break; case 113: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_d; break; case 1: op = rv_op_fclass_d; break; } break; case 115: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_q; break; case 1: op = rv_op_fclass_q; break; } break; case 120: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_s_x; break; } break; case 121: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_d_x; break; } break; case 123: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_q_x; break; } break; @@ -3224,9 +3277,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: op = rv_op_vxor_vv; break; case 12: op = rv_op_vrgather_vv; break; case 14: op = rv_op_vrgatherei16_vv; break; - case 16: if (((inst >> 25) & 1) == 0) op = rv_op_vadc_vvm; break; + case 16: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vadc_vvm; + } + break; case 17: op = rv_op_vmadc_vvm; break; - case 18: if (((inst >> 25) & 1) == 0) op = rv_op_vsbc_vvm; break; + case 18: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vsbc_vvm; + } + break; case 19: op = rv_op_vmsbc_vvm; break; case 23: if (((inst >> 20) & 0b111111) == 32) @@ -3373,7 +3434,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 2: op = rv_op_vmsof_m; break; case 3: op = rv_op_vmsif_m; break; case 16: op = rv_op_viota_m; break; - case 17: if (((inst >> 20) & 0b11111) == 0) op = rv_op_vid_v; break; + case 17: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_vid_v; + } + break; } break; case 23: if ((inst >> 25) & 1) op = rv_op_vcompress_vm; break; @@ -3423,7 +3488,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 12: op = rv_op_vrgather_vi; break; case 14: op = rv_op_vslideup_vi; break; case 15: op = rv_op_vslidedown_vi; break; - case 16: if (((inst >> 25) & 1) == 0) op = rv_op_vadc_vim; break; + case 16: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vadc_vim; + } + break; case 17: op = rv_op_vmadc_vim; break; case 23: if (((inst >> 20) & 0b111111) == 32) @@ -3473,9 +3542,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 12: op = rv_op_vrgather_vx; break; case 14: op = rv_op_vslideup_vx; break; case 15: op = rv_op_vslidedown_vx; break; - case 16: if (((inst >> 25) & 1) == 0) op = rv_op_vadc_vxm; break; + case 16: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vadc_vxm; + } + break; case 17: op = rv_op_vmadc_vxm; break; - case 18: if (((inst >> 25) & 1) == 0) op = rv_op_vsbc_vxm; break; + case 18: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vsbc_vxm; + } + break; case 19: op = rv_op_vmsbc_vxm; break; case 23: if (((inst >> 20) & 0b111111) == 32) @@ -3646,7 +3723,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 28: switch (((inst >> 12) & 0b111)) { case 0: - switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) { + switch (((inst >> 20) & 0b111111100000) | + ((inst >> 7) & 0b000000011111)) { case 0: switch (((inst >> 15) & 0b1111111111)) { case 0: op = rv_op_ecall; break; @@ -3696,7 +3774,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 30: - switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + switch (((inst >> 22) & 0b1111111000) | + ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_addd; break; case 1: op = rv_op_slld; break; case 5: op = rv_op_srld; break; From 3bd87176eeb3dee494ccaac56d9f77160c87bb9f Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Tue, 23 May 2023 17:35:39 +0800 Subject: [PATCH 306/412] disas/riscv.c: Remove redundant parentheses Remove redundant parenthese and fix multi-line comments. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-Id: <20230523093539.203909-9-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- disas/riscv.c | 219 +++++++++++++++++++++++++------------------------- 1 file changed, 110 insertions(+), 109 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index bc433c4120..5005364aba 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2390,9 +2390,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; - switch (((inst >> 0) & 0b11)) { + switch ((inst >> 0) & 0b11) { case 0: - switch (((inst >> 13) & 0b111)) { + switch ((inst >> 13) & 0b111) { case 0: op = rv_op_c_addi4spn; break; case 1: if (isa == rv128) { @@ -2445,9 +2445,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 1: - switch (((inst >> 13) & 0b111)) { + switch ((inst >> 13) & 0b111) { case 0: - switch (((inst >> 2) & 0b11111111111)) { + switch ((inst >> 2) & 0b11111111111) { case 0: op = rv_op_c_nop; break; default: op = rv_op_c_addi; break; } @@ -2461,13 +2461,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 2: op = rv_op_c_li; break; case 3: - switch (((inst >> 7) & 0b11111)) { + switch ((inst >> 7) & 0b11111) { case 2: op = rv_op_c_addi16sp; break; default: op = rv_op_c_lui; break; } break; case 4: - switch (((inst >> 10) & 0b11)) { + switch ((inst >> 10) & 0b11) { case 0: op = rv_op_c_srli; break; @@ -2504,7 +2504,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 2: - switch (((inst >> 13) & 0b111)) { + switch ((inst >> 13) & 0b111) { case 0: op = rv_op_c_slli; break; @@ -2524,17 +2524,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 4: - switch (((inst >> 12) & 0b1)) { + switch ((inst >> 12) & 0b1) { case 0: - switch (((inst >> 2) & 0b11111)) { + switch ((inst >> 2) & 0b11111) { case 0: op = rv_op_c_jr; break; default: op = rv_op_c_mv; break; } break; case 1: - switch (((inst >> 2) & 0b11111)) { + switch ((inst >> 2) & 0b11111) { case 0: - switch (((inst >> 7) & 0b11111)) { + switch ((inst >> 7) & 0b11111) { case 0: op = rv_op_c_ebreak; break; default: op = rv_op_c_jalr; break; } @@ -2608,9 +2608,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - switch (((inst >> 2) & 0b11111)) { + switch ((inst >> 2) & 0b11111) { case 0: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_lb; break; case 1: op = rv_op_lh; break; case 2: op = rv_op_lw; break; @@ -2622,17 +2622,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 1: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: - switch (((inst >> 20) & 0b111111111111)) { + switch ((inst >> 20) & 0b111111111111) { case 40: op = rv_op_vl1re8_v; break; case 552: op = rv_op_vl2re8_v; break; case 1576: op = rv_op_vl4re8_v; break; case 3624: op = rv_op_vl8re8_v; break; } - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle8_v; break; case 11: op = rv_op_vlm_v; break; case 16: op = rv_op_vle8ff_v; break; @@ -2647,15 +2647,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_fld; break; case 4: op = rv_op_flq; break; case 5: - switch (((inst >> 20) & 0b111111111111)) { + switch ((inst >> 20) & 0b111111111111) { case 40: op = rv_op_vl1re16_v; break; case 552: op = rv_op_vl2re16_v; break; case 1576: op = rv_op_vl4re16_v; break; case 3624: op = rv_op_vl8re16_v; break; } - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle16_v; break; case 16: op = rv_op_vle16ff_v; break; } @@ -2666,15 +2666,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: - switch (((inst >> 20) & 0b111111111111)) { + switch ((inst >> 20) & 0b111111111111) { case 40: op = rv_op_vl1re32_v; break; case 552: op = rv_op_vl2re32_v; break; case 1576: op = rv_op_vl4re32_v; break; case 3624: op = rv_op_vl8re32_v; break; } - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle32_v; break; case 16: op = rv_op_vle32ff_v; break; } @@ -2685,15 +2685,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 7: - switch (((inst >> 20) & 0b111111111111)) { + switch ((inst >> 20) & 0b111111111111) { case 40: op = rv_op_vl1re64_v; break; case 552: op = rv_op_vl2re64_v; break; case 1576: op = rv_op_vl4re64_v; break; case 3624: op = rv_op_vl8re64_v; break; } - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle64_v; break; case 16: op = rv_op_vle64ff_v; break; } @@ -2706,25 +2706,25 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fence; break; case 1: op = rv_op_fence_i; break; case 2: op = rv_op_lq; break; } break; case 4: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addi; break; case 1: - switch (((inst >> 27) & 0b11111)) { + switch ((inst >> 27) & 0b11111) { case 0b00000: op = rv_op_slli; break; case 0b00001: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0001111: op = rv_op_zip; break; } break; case 0b00010: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0000000: op = rv_op_sha256sum0; break; case 0b0000001: op = rv_op_sha256sum1; break; case 0b0000010: op = rv_op_sha256sig0; break; @@ -2739,7 +2739,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 0b00101: op = rv_op_bseti; break; case 0b00110: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0000000: op = rv_op_aes64im; break; default: if (((inst >> 24) & 0b0111) == 0b001) { @@ -2751,7 +2751,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0b01001: op = rv_op_bclri; break; case 0b01101: op = rv_op_binvi; break; case 0b01100: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0000000: op = rv_op_clz; break; case 0b0000001: op = rv_op_ctz; break; case 0b0000010: op = rv_op_cpop; break; @@ -2766,10 +2766,10 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_sltiu; break; case 4: op = rv_op_xori; break; case 5: - switch (((inst >> 27) & 0b11111)) { + switch ((inst >> 27) & 0b11111) { case 0b00000: op = rv_op_srli; break; case 0b00001: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0001111: op = rv_op_unzip; break; } break; @@ -2792,10 +2792,10 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 5: op = rv_op_auipc; break; case 6: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addiw; break; case 1: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_slliw; break; case 2: op = rv_op_slli_uw; break; case 24: @@ -2808,7 +2808,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 5: - switch (((inst >> 25) & 0b1111111)) { + switch ((inst >> 25) & 0b1111111) { case 0: op = rv_op_srliw; break; case 32: op = rv_op_sraiw; break; case 48: op = rv_op_roriw; break; @@ -2817,7 +2817,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 8: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_sb; break; case 1: op = rv_op_sh; break; case 2: op = rv_op_sw; break; @@ -2826,17 +2826,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 9: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: - switch (((inst >> 20) & 0b111111111111)) { + switch ((inst >> 20) & 0b111111111111) { case 40: op = rv_op_vs1r_v; break; case 552: op = rv_op_vs2r_v; break; case 1576: op = rv_op_vs4r_v; break; case 3624: op = rv_op_vs8r_v; break; } - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse8_v; break; case 11: op = rv_op_vsm_v; break; } @@ -2850,9 +2850,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_fsd; break; case 4: op = rv_op_fsq; break; case 5: - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse16_v; break; } break; @@ -2862,9 +2862,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse32_v; break; } break; @@ -2874,9 +2874,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 7: - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse64_v; break; } break; @@ -2897,17 +2897,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: op = rv_op_amoswap_d; break; case 12: op = rv_op_amoswap_q; break; case 18: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_lr_w; break; } break; case 19: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_lr_d; break; } break; case 20: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_lr_q; break; } break; @@ -3039,35 +3039,35 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 16: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fmadd_s; break; case 1: op = rv_op_fmadd_d; break; case 3: op = rv_op_fmadd_q; break; } break; case 17: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fmsub_s; break; case 1: op = rv_op_fmsub_d; break; case 3: op = rv_op_fmsub_q; break; } break; case 18: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fnmsub_s; break; case 1: op = rv_op_fnmsub_d; break; case 3: op = rv_op_fnmsub_q; break; } break; case 19: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fnmadd_s; break; case 1: op = rv_op_fnmadd_d; break; case 3: op = rv_op_fnmadd_q; break; } break; case 20: - switch (((inst >> 25) & 0b1111111)) { + switch ((inst >> 25) & 0b1111111) { case 0: op = rv_op_fadd_s; break; case 1: op = rv_op_fadd_d; break; case 3: op = rv_op_fadd_q; break; @@ -3081,100 +3081,100 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 13: op = rv_op_fdiv_d; break; case 15: op = rv_op_fdiv_q; break; case 16: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fsgnj_s; break; case 1: op = rv_op_fsgnjn_s; break; case 2: op = rv_op_fsgnjx_s; break; } break; case 17: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fsgnj_d; break; case 1: op = rv_op_fsgnjn_d; break; case 2: op = rv_op_fsgnjx_d; break; } break; case 19: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fsgnj_q; break; case 1: op = rv_op_fsgnjn_q; break; case 2: op = rv_op_fsgnjx_q; break; } break; case 20: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_s; break; case 1: op = rv_op_fmax_s; break; } break; case 21: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_d; break; case 1: op = rv_op_fmax_d; break; } break; case 23: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_q; break; case 1: op = rv_op_fmax_q; break; } break; case 32: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 1: op = rv_op_fcvt_s_d; break; case 3: op = rv_op_fcvt_s_q; break; } break; case 33: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_d_s; break; case 3: op = rv_op_fcvt_d_q; break; } break; case 35: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_q_s; break; case 1: op = rv_op_fcvt_q_d; break; } break; case 44: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fsqrt_s; break; } break; case 45: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fsqrt_d; break; } break; case 47: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fsqrt_q; break; } break; case 80: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fle_s; break; case 1: op = rv_op_flt_s; break; case 2: op = rv_op_feq_s; break; } break; case 81: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fle_d; break; case 1: op = rv_op_flt_d; break; case 2: op = rv_op_feq_d; break; } break; case 83: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fle_q; break; case 1: op = rv_op_flt_q; break; case 2: op = rv_op_feq_q; break; } break; case 96: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_w_s; break; case 1: op = rv_op_fcvt_wu_s; break; case 2: op = rv_op_fcvt_l_s; break; @@ -3182,7 +3182,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 97: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_w_d; break; case 1: op = rv_op_fcvt_wu_d; break; case 2: op = rv_op_fcvt_l_d; break; @@ -3190,7 +3190,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 99: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_w_q; break; case 1: op = rv_op_fcvt_wu_q; break; case 2: op = rv_op_fcvt_l_q; break; @@ -3198,7 +3198,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 104: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_s_w; break; case 1: op = rv_op_fcvt_s_wu; break; case 2: op = rv_op_fcvt_s_l; break; @@ -3206,7 +3206,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 105: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_d_w; break; case 1: op = rv_op_fcvt_d_wu; break; case 2: op = rv_op_fcvt_d_l; break; @@ -3214,7 +3214,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 107: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_q_w; break; case 1: op = rv_op_fcvt_q_wu; break; case 2: op = rv_op_fcvt_q_l; break; @@ -3263,9 +3263,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 21: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vadd_vv; break; case 2: op = rv_op_vsub_vv; break; case 4: op = rv_op_vminu_vv; break; @@ -3320,7 +3320,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 1: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vfadd_vv; break; case 1: op = rv_op_vfredusum_vs; break; case 2: op = rv_op_vfsub_vv; break; @@ -3333,12 +3333,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 9: op = rv_op_vfsgnjn_vv; break; case 10: op = rv_op_vfsgnjx_vv; break; case 16: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_f_s; break; } break; case 18: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_vfcvt_xu_f_v; break; case 1: op = rv_op_vfcvt_x_f_v; break; case 2: op = rv_op_vfcvt_f_xu_v; break; @@ -3363,7 +3363,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 19: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_vfsqrt_v; break; case 4: op = rv_op_vfrsqrt7_v; break; case 5: op = rv_op_vfrec7_v; break; @@ -3398,7 +3398,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 2: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vredsum_vs; break; case 1: op = rv_op_vredand_vs; break; case 2: op = rv_op_vredor_vs; break; @@ -3412,14 +3412,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 10: op = rv_op_vasubu_vv; break; case 11: op = rv_op_vasub_vv; break; case 16: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vmv_x_s; break; case 16: op = rv_op_vcpop_m; break; case 17: op = rv_op_vfirst_m; break; } break; case 18: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 2: op = rv_op_vzext_vf8; break; case 3: op = rv_op_vsext_vf8; break; case 4: op = rv_op_vzext_vf4; break; @@ -3429,7 +3429,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 20: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 1: op = rv_op_vmsbf_m; break; case 2: op = rv_op_vmsof_m; break; case 3: op = rv_op_vmsif_m; break; @@ -3479,7 +3479,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vadd_vi; break; case 3: op = rv_op_vrsub_vi; break; case 9: op = rv_op_vand_vi; break; @@ -3510,7 +3510,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 33: op = rv_op_vsadd_vi; break; case 37: op = rv_op_vsll_vi; break; case 39: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_vmv1r_v; break; case 1: op = rv_op_vmv2r_v; break; case 3: op = rv_op_vmv4r_v; break; @@ -3528,7 +3528,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 4: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vadd_vx; break; case 2: op = rv_op_vsub_vx; break; case 3: op = rv_op_vrsub_vx; break; @@ -3585,7 +3585,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 5: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vfadd_vf; break; case 2: op = rv_op_vfsub_vf; break; case 4: op = rv_op_vfmin_vf; break; @@ -3596,7 +3596,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 14: op = rv_op_vfslide1up_vf; break; case 15: op = rv_op_vfslide1down_vf; break; case 16: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_s_f; break; } break; @@ -3636,7 +3636,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 8: op = rv_op_vaaddu_vx; break; case 9: op = rv_op_vaadd_vx; break; case 10: op = rv_op_vasubu_vx; break; @@ -3644,7 +3644,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 14: op = rv_op_vslide1up_vx; break; case 15: op = rv_op_vslide1down_vx; break; case 16: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vmv_s_x; break; } break; @@ -3689,15 +3689,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 22: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addid; break; case 1: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_sllid; break; } break; case 5: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_srlid; break; case 16: op = rv_op_sraid; break; } @@ -3705,7 +3705,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 24: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_beq; break; case 1: op = rv_op_bne; break; case 4: op = rv_op_blt; break; @@ -3715,33 +3715,33 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 25: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_jalr; break; } break; case 27: op = rv_op_jal; break; case 28: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) { case 0: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 0: op = rv_op_ecall; break; case 32: op = rv_op_ebreak; break; case 64: op = rv_op_uret; break; } break; case 256: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 2: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_sret; break; } break; case 4: op = rv_op_sfence_vm; break; case 5: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_wfi; break; } break; @@ -3749,17 +3749,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 288: op = rv_op_sfence_vma; break; case 512: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 64: op = rv_op_hret; break; } break; case 768: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 64: op = rv_op_mret; break; } break; case 1952: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 576: op = rv_op_dret; break; } break; @@ -4611,7 +4611,8 @@ static size_t inst_length(rv_inst inst) { /* NOTE: supports maximum instruction size of 64-bits */ - /* instruction length coding + /* + * instruction length coding * * aa - 16 bit aa != 11 * bbb11 - 32 bit bbb != 111 From bfc4f9e351e77c69fe21315815bc5db8ef7c22df Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:18 +0800 Subject: [PATCH 307/412] target/riscv: Fix target address to update badaddr Compute the target address before storing it into badaddr when mis-aligned exception is triggered. Use a target_pc temp to store the target address to avoid the confusing operation that udpate target address into cpu_pc before misalign check, then update it into badaddr and restore cpu_pc to current pc if exception is triggered. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvi.c.inc | 23 ++++++++++++++++------- target/riscv/insn_trans/trans_rvzce.c.inc | 4 ++-- target/riscv/translate.c | 21 ++++++++++----------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index d794247f40..009dc96dbd 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -51,25 +51,30 @@ static bool trans_jal(DisasContext *ctx, arg_jal *a) static bool trans_jalr(DisasContext *ctx, arg_jalr *a) { TCGLabel *misaligned = NULL; + TCGv target_pc = tcg_temp_new(); - tcg_gen_addi_tl(cpu_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); - tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); + tcg_gen_addi_tl(target_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); + tcg_gen_andi_tl(target_pc, target_pc, (target_ulong)-2); + + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target_pc, target_pc); + } - gen_set_pc(ctx, cpu_pc); if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { TCGv t0 = tcg_temp_new(); misaligned = gen_new_label(); - tcg_gen_andi_tl(t0, cpu_pc, 0x2); + tcg_gen_andi_tl(t0, target_pc, 0x2); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); } gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn); + tcg_gen_mov_tl(cpu_pc, target_pc); lookup_and_goto_ptr(ctx); if (misaligned) { gen_set_label(misaligned); - gen_exception_inst_addr_mis(ctx); + gen_exception_inst_addr_mis(ctx, target_pc); } ctx->base.is_jmp = DISAS_NORETURN; @@ -153,6 +158,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) TCGLabel *l = gen_new_label(); TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN); TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN); + target_ulong next_pc; if (get_xl(ctx) == MXL_RV128) { TCGv src1h = get_gprh(ctx, a->rs1); @@ -169,10 +175,13 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) gen_set_label(l); /* branch taken */ + next_pc = ctx->base.pc_next + a->imm; if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca && - ((ctx->base.pc_next + a->imm) & 0x3)) { + (next_pc & 0x3)) { /* misaligned */ - gen_exception_inst_addr_mis(ctx); + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, next_pc); + gen_exception_inst_addr_mis(ctx, target_pc); } else { gen_goto_tb(ctx, 0, ctx->base.pc_next + a->imm); } diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index a727169a4b..5732d782f7 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -202,8 +202,8 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) } if (ret) { - TCGv ret_addr = get_gpr(ctx, xRA, EXT_NONE); - gen_set_pc(ctx, ret_addr); + TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN); + tcg_gen_mov_tl(cpu_pc, ret_addr); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 29f1fb3995..6fbdb50c5d 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -224,21 +224,18 @@ static void decode_save_opc(DisasContext *ctx) ctx->insn_start = NULL; } -static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) +static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, + target_ulong dest) { if (get_xl(ctx) == MXL_RV32) { dest = (int32_t)dest; } - tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_movi_tl(target, dest); } -static void gen_set_pc(DisasContext *ctx, TCGv dest) +static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) { - if (get_xl(ctx) == MXL_RV32) { - tcg_gen_ext32s_tl(cpu_pc, dest); - } else { - tcg_gen_mov_tl(cpu_pc, dest); - } + gen_pc_plus_diff(cpu_pc, ctx, dest); } static void generate_exception(DisasContext *ctx, int excp) @@ -259,9 +256,9 @@ static void gen_exception_illegal(DisasContext *ctx) } } -static void gen_exception_inst_addr_mis(DisasContext *ctx) +static void gen_exception_inst_addr_mis(DisasContext *ctx, TCGv target) { - tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); + tcg_gen_st_tl(target, cpu_env, offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); } @@ -553,7 +550,9 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) next_pc = ctx->base.pc_next + imm; if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { if ((next_pc & 0x3) != 0) { - gen_exception_inst_addr_mis(ctx); + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, next_pc); + gen_exception_inst_addr_mis(ctx, target_pc); return; } } From 8ef23a329accd36394ffbddf87cc18ef0209dd6b Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:19 +0800 Subject: [PATCH 308/412] target/riscv: Introduce cur_insn_len into DisasContext Use cur_insn_len to store the length of the current instruction to prepare for PC-relative translation. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 6fbdb50c5d..ea63d20eef 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -61,6 +61,7 @@ typedef struct DisasContext { DisasContextBase base; /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; + target_ulong cur_insn_len; target_ulong priv_ver; RISCVMXL misa_mxl_max; RISCVMXL xl; @@ -1116,8 +1117,9 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) }; ctx->virt_inst_excp = false; + ctx->cur_insn_len = insn_len(opcode); /* Check for compressed insn */ - if (insn_len(opcode) == 2) { + if (ctx->cur_insn_len == 2) { ctx->opcode = opcode; ctx->pc_succ_insn = ctx->base.pc_next + 2; /* From 1df8497b9d21a15a2dac1ecb49c9f07096e31ce2 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:20 +0800 Subject: [PATCH 309/412] target/riscv: Change gen_goto_tb to work on displacements Reduce reliance on absolute value to prepare for PC-relative translation. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvi.c.inc | 4 ++-- target/riscv/translate.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 009dc96dbd..321885f951 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -171,7 +171,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) } else { tcg_gen_brcond_tl(cond, src1, src2, l); } - gen_goto_tb(ctx, 1, ctx->pc_succ_insn); + gen_goto_tb(ctx, 1, ctx->cur_insn_len); gen_set_label(l); /* branch taken */ @@ -183,7 +183,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) gen_pc_plus_diff(target_pc, ctx, next_pc); gen_exception_inst_addr_mis(ctx, target_pc); } else { - gen_goto_tb(ctx, 0, ctx->base.pc_next + a->imm); + gen_goto_tb(ctx, 0, a->imm); } ctx->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index ea63d20eef..33c666d74e 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -283,8 +283,10 @@ static void exit_tb(DisasContext *ctx) tcg_gen_exit_tb(NULL, 0); } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) { + target_ulong dest = ctx->base.pc_next + diff; + /* * Under itrigger, instruction executes one by one like singlestep, * direct block chain benefits will be small. @@ -559,7 +561,7 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) } gen_set_gpri(ctx, rd, ctx->pc_succ_insn); - gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ + gen_goto_tb(ctx, 0, imm); /* must use this for safety */ ctx->base.is_jmp = DISAS_NORETURN; } @@ -1231,7 +1233,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: - gen_goto_tb(ctx, 0, ctx->base.pc_next); + gen_goto_tb(ctx, 0, 0); break; case DISAS_NORETURN: break; From 022c7550d994496d38c035e2290f9f8979065bad Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:21 +0800 Subject: [PATCH 310/412] target/riscv: Change gen_set_pc_imm to gen_update_pc Reduce reliance on absolute values(by passing pc difference) to prepare for PC-relative translation. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-5-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_privileged.c.inc | 2 +- target/riscv/insn_trans/trans_rvi.c.inc | 6 +++--- target/riscv/insn_trans/trans_rvv.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvzawrs.c.inc | 2 +- target/riscv/insn_trans/trans_xthead.c.inc | 2 +- target/riscv/translate.c | 10 +++++----- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 528baa1652..dc14d7fc7a 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -108,7 +108,7 @@ static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY decode_save_opc(ctx); - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); gen_helper_wfi(cpu_env); return true; #else diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 321885f951..4837e133cc 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -777,7 +777,7 @@ static bool trans_pause(DisasContext *ctx, arg_pause *a) * PAUSE is a no-op in QEMU, * end the TB and return to main loop */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; @@ -801,7 +801,7 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) * FENCE_I is a no-op in QEMU, * however we need to end the translation block */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -812,7 +812,7 @@ static bool do_csr_post(DisasContext *ctx) /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ decode_save_opc(ctx); /* We may have changed important cpu state -- exit to main loop. */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 6c07eebc52..c2f7527f53 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -169,7 +169,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) gen_set_gpr(s, rd, dst); mark_vs_dirty(s); - gen_set_pc_imm(s, s->pc_succ_insn); + gen_update_pc(s, s->cur_insn_len); lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; return true; @@ -188,7 +188,7 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) gen_helper_vsetvl(dst, cpu_env, s1, s2); gen_set_gpr(s, rd, dst); mark_vs_dirty(s); - gen_set_pc_imm(s, s->pc_succ_insn); + gen_update_pc(s, s->cur_insn_len); lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/insn_trans/trans_rvzawrs.c.inc b/target/riscv/insn_trans/trans_rvzawrs.c.inc index 8254e7dfe2..32efbff4d5 100644 --- a/target/riscv/insn_trans/trans_rvzawrs.c.inc +++ b/target/riscv/insn_trans/trans_rvzawrs.c.inc @@ -33,7 +33,7 @@ static bool trans_wrs(DisasContext *ctx) /* Clear the load reservation (if any). */ tcg_gen_movi_tl(load_res, -1); - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; diff --git a/target/riscv/insn_trans/trans_xthead.c.inc b/target/riscv/insn_trans/trans_xthead.c.inc index 3e13b1d74d..da093a4cec 100644 --- a/target/riscv/insn_trans/trans_xthead.c.inc +++ b/target/riscv/insn_trans/trans_xthead.c.inc @@ -999,7 +999,7 @@ static void gen_th_sync_local(DisasContext *ctx) * Emulate out-of-order barriers with pipeline flush * by exiting the translation block. */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); + gen_update_pc(ctx, ctx->cur_insn_len); tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 33c666d74e..eda022d10b 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -234,14 +234,14 @@ static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, tcg_gen_movi_tl(target, dest); } -static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) +static void gen_update_pc(DisasContext *ctx, target_long diff) { - gen_pc_plus_diff(cpu_pc, ctx, dest); + gen_pc_plus_diff(cpu_pc, ctx, ctx->base.pc_next + diff); } static void generate_exception(DisasContext *ctx, int excp) { - gen_set_pc_imm(ctx, ctx->base.pc_next); + gen_update_pc(ctx, 0); gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); ctx->base.is_jmp = DISAS_NORETURN; } @@ -293,10 +293,10 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) */ if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) { tcg_gen_goto_tb(n); - gen_set_pc_imm(ctx, dest); + gen_update_pc(ctx, diff); tcg_gen_exit_tb(ctx->base.tb, n); } else { - gen_set_pc_imm(ctx, dest); + gen_update_pc(ctx, diff); lookup_and_goto_ptr(ctx); } } From 227fb82f99ac2d147c15342b2c83c7f6c28f20d2 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:22 +0800 Subject: [PATCH 311/412] target/riscv: Use true diff for gen_pc_plus_diff Reduce reliance on absolute values by using true pc difference for gen_pc_plus_diff() to prepare for PC-relative translation. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-6-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvi.c.inc | 6 ++---- target/riscv/insn_trans/trans_rvzce.c.inc | 2 +- target/riscv/translate.c | 13 ++++++------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 4837e133cc..2d350cfbd7 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -158,7 +158,6 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) TCGLabel *l = gen_new_label(); TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN); TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN); - target_ulong next_pc; if (get_xl(ctx) == MXL_RV128) { TCGv src1h = get_gprh(ctx, a->rs1); @@ -175,12 +174,11 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) gen_set_label(l); /* branch taken */ - next_pc = ctx->base.pc_next + a->imm; if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca && - (next_pc & 0x3)) { + (a->imm & 0x3)) { /* misaligned */ TCGv target_pc = tcg_temp_new(); - gen_pc_plus_diff(target_pc, ctx, next_pc); + gen_pc_plus_diff(target_pc, ctx, a->imm); gen_exception_inst_addr_mis(ctx, target_pc); } else { gen_goto_tb(ctx, 0, a->imm); diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index 5732d782f7..450b79dcbc 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -297,7 +297,7 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) * Update pc to current for the non-unwinding exception * that might come from cpu_ld*_code() in the helper. */ - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + gen_update_pc(ctx, 0); gen_helper_cm_jalt(cpu_pc, cpu_env, tcg_constant_i32(a->index)); /* c.jt vs c.jalt depends on the index. */ diff --git a/target/riscv/translate.c b/target/riscv/translate.c index eda022d10b..7fb4cbe84c 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -226,8 +226,10 @@ static void decode_save_opc(DisasContext *ctx) } static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, - target_ulong dest) + target_long diff) { + target_ulong dest = ctx->base.pc_next + diff; + if (get_xl(ctx) == MXL_RV32) { dest = (int32_t)dest; } @@ -236,7 +238,7 @@ static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, static void gen_update_pc(DisasContext *ctx, target_long diff) { - gen_pc_plus_diff(cpu_pc, ctx, ctx->base.pc_next + diff); + gen_pc_plus_diff(cpu_pc, ctx, diff); } static void generate_exception(DisasContext *ctx, int excp) @@ -547,14 +549,11 @@ static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { - target_ulong next_pc; - /* check misaligned: */ - next_pc = ctx->base.pc_next + imm; if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { - if ((next_pc & 0x3) != 0) { + if ((imm & 0x3) != 0) { TCGv target_pc = tcg_temp_new(); - gen_pc_plus_diff(target_pc, ctx, next_pc); + gen_pc_plus_diff(target_pc, ctx, imm); gen_exception_inst_addr_mis(ctx, target_pc); return; } From 356c13f94dbff3f32e9d3615f6caa35a2a324d8d Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:23 +0800 Subject: [PATCH 312/412] target/riscv: Enable PC-relative translation Add a base pc_save for PC-relative translation(CF_PCREL). Diable the directly sync pc from tb by riscv_cpu_synchronize_from_tb. Use gen_pc_plus_diff to get the pc-relative address. Enable CF_PCREL in System mode. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-7-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 31 ++++++++++----- target/riscv/insn_trans/trans_rvi.c.inc | 12 +++++- target/riscv/insn_trans/trans_rvzce.c.inc | 4 +- target/riscv/translate.c | 47 +++++++++++++++++++---- 4 files changed, 74 insertions(+), 20 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 938c7bd87b..881bddf393 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -721,16 +721,18 @@ static vaddr riscv_cpu_get_pc(CPUState *cs) static void riscv_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); + if (!(tb_cflags(tb) & CF_PCREL)) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - if (xl == MXL_RV32) { - env->pc = (int32_t) tb->pc; - } else { - env->pc = tb->pc; + if (xl == MXL_RV32) { + env->pc = (int32_t) tb->pc; + } else { + env->pc = tb->pc; + } } } @@ -756,11 +758,18 @@ static void riscv_restore_state_to_opc(CPUState *cs, RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); + target_ulong pc; + + if (tb_cflags(tb) & CF_PCREL) { + pc = (env->pc & TARGET_PAGE_MASK) | data[0]; + } else { + pc = data[0]; + } if (xl == MXL_RV32) { - env->pc = (int32_t)data[0]; + env->pc = (int32_t)pc; } else { - env->pc = data[0]; + env->pc = pc; } env->bins = data[1]; } @@ -1343,6 +1352,8 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } #ifndef CONFIG_USER_ONLY + cs->tcg_cflags |= CF_PCREL; + if (cpu->cfg.ext_sstc) { riscv_timer_init(cpu); } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 2d350cfbd7..297142208e 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -38,7 +38,9 @@ static bool trans_lui(DisasContext *ctx, arg_lui *a) static bool trans_auipc(DisasContext *ctx, arg_auipc *a) { - gen_set_gpri(ctx, a->rd, a->imm + ctx->base.pc_next); + TCGv target_pc = dest_gpr(ctx, a->rd); + gen_pc_plus_diff(target_pc, ctx, a->imm); + gen_set_gpr(ctx, a->rd, target_pc); return true; } @@ -52,6 +54,7 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) { TCGLabel *misaligned = NULL; TCGv target_pc = tcg_temp_new(); + TCGv succ_pc = dest_gpr(ctx, a->rd); tcg_gen_addi_tl(target_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); tcg_gen_andi_tl(target_pc, target_pc, (target_ulong)-2); @@ -68,7 +71,9 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); } - gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, a->rd, succ_pc); + tcg_gen_mov_tl(cpu_pc, target_pc); lookup_and_goto_ptr(ctx); @@ -158,6 +163,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) TCGLabel *l = gen_new_label(); TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN); TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN); + target_ulong orig_pc_save = ctx->pc_save; if (get_xl(ctx) == MXL_RV128) { TCGv src1h = get_gprh(ctx, a->rs1); @@ -171,6 +177,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) tcg_gen_brcond_tl(cond, src1, src2, l); } gen_goto_tb(ctx, 1, ctx->cur_insn_len); + ctx->pc_save = orig_pc_save; gen_set_label(l); /* branch taken */ @@ -183,6 +190,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) } else { gen_goto_tb(ctx, 0, a->imm); } + ctx->pc_save = -1; ctx->base.is_jmp = DISAS_NORETURN; return true; diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index 450b79dcbc..8d8a64f493 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -302,7 +302,9 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) /* c.jt vs c.jalt depends on the index. */ if (a->index >= 32) { - gen_set_gpri(ctx, xRA, ctx->pc_succ_insn); + TCGv succ_pc = dest_gpr(ctx, xRA); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, xRA, succ_pc); } tcg_gen_lookup_and_goto_ptr(); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 7fb4cbe84c..bd2da9b079 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -62,6 +62,7 @@ typedef struct DisasContext { /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; target_ulong cur_insn_len; + target_ulong pc_save; target_ulong priv_ver; RISCVMXL misa_mxl_max; RISCVMXL xl; @@ -230,15 +231,24 @@ static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, { target_ulong dest = ctx->base.pc_next + diff; - if (get_xl(ctx) == MXL_RV32) { - dest = (int32_t)dest; + assert(ctx->pc_save != -1); + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(target, cpu_pc, dest - ctx->pc_save); + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target, target); + } + } else { + if (get_xl(ctx) == MXL_RV32) { + dest = (int32_t)dest; + } + tcg_gen_movi_tl(target, dest); } - tcg_gen_movi_tl(target, dest); } static void gen_update_pc(DisasContext *ctx, target_long diff) { gen_pc_plus_diff(cpu_pc, ctx, diff); + ctx->pc_save = ctx->base.pc_next + diff; } static void generate_exception(DisasContext *ctx, int excp) @@ -294,8 +304,21 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) * direct block chain benefits will be small. */ if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) { - tcg_gen_goto_tb(n); - gen_update_pc(ctx, diff); + /* + * For pcrel, the pc must always be up-to-date on entry to + * the linked TB, so that it can use simple additions for all + * further adjustments. For !pcrel, the linked TB is compiled + * to know its full virtual address, so we can delay the + * update to pc to the unlinked path. A long chain of links + * can thus avoid many updates to the PC. + */ + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + gen_update_pc(ctx, diff); + tcg_gen_goto_tb(n); + } else { + tcg_gen_goto_tb(n); + gen_update_pc(ctx, diff); + } tcg_gen_exit_tb(ctx->base.tb, n); } else { gen_update_pc(ctx, diff); @@ -549,6 +572,8 @@ static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { + TCGv succ_pc = dest_gpr(ctx, rd); + /* check misaligned: */ if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { if ((imm & 0x3) != 0) { @@ -559,7 +584,9 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) } } - gen_set_gpri(ctx, rd, ctx->pc_succ_insn); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, rd, succ_pc); + gen_goto_tb(ctx, 0, imm); /* must use this for safety */ ctx->base.is_jmp = DISAS_NORETURN; } @@ -1157,6 +1184,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); uint32_t tb_flags = ctx->base.tb->flags; + ctx->pc_save = ctx->base.pc_first; ctx->pc_succ_insn = ctx->base.pc_first; ctx->priv = FIELD_EX32(tb_flags, TB_FLAGS, PRIV); ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); @@ -1192,8 +1220,13 @@ static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + target_ulong pc_next = ctx->base.pc_next; - tcg_gen_insn_start(ctx->base.pc_next, 0); + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_next &= ~TARGET_PAGE_MASK; + } + + tcg_gen_insn_start(pc_next, 0); ctx->insn_start = tcg_last_op(); } From 90b0aecaf9148cc3a78abee7deb824ca6cf13ad5 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 26 May 2023 15:21:24 +0800 Subject: [PATCH 313/412] target/riscv: Remove pc_succ_insn from DisasContext pc_succ_insn is no longer useful after the introduce of cur_insn_len and all pc related value use diff value instead of absolute value. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20230526072124.298466-8-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index bd2da9b079..8a33da811e 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -59,8 +59,6 @@ typedef enum { typedef struct DisasContext { DisasContextBase base; - /* pc_succ_insn points to the instruction following base.pc_next */ - target_ulong pc_succ_insn; target_ulong cur_insn_len; target_ulong pc_save; target_ulong priv_ver; @@ -1149,7 +1147,6 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) /* Check for compressed insn */ if (ctx->cur_insn_len == 2) { ctx->opcode = opcode; - ctx->pc_succ_insn = ctx->base.pc_next + 2; /* * The Zca extension is added as way to refer to instructions in the C * extension that do not include the floating-point loads and stores @@ -1164,7 +1161,6 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) translator_lduw(env, &ctx->base, ctx->base.pc_next + 2)); ctx->opcode = opcode32; - ctx->pc_succ_insn = ctx->base.pc_next + 4; for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { if (decoders[i].guard_func(ctx) && @@ -1185,7 +1181,6 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) uint32_t tb_flags = ctx->base.tb->flags; ctx->pc_save = ctx->base.pc_first; - ctx->pc_succ_insn = ctx->base.pc_first; ctx->priv = FIELD_EX32(tb_flags, TB_FLAGS, PRIV); ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); ctx->mstatus_fs = FIELD_EX32(tb_flags, TB_FLAGS, FS); @@ -1238,7 +1233,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) ctx->ol = ctx->xl; decode_opc(env, ctx, opcode16); - ctx->base.pc_next = ctx->pc_succ_insn; + ctx->base.pc_next += ctx->cur_insn_len; /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { From 4263e270a240cee6e17ae76a752ab2a76493a779 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 1 Jun 2023 10:29:08 +0530 Subject: [PATCH 314/412] hw/riscv: virt: Assume M-mode FW in pflash0 only when "-bios none" Currently, virt machine supports two pflash instances each with 32MB size. However, the first pflash is always assumed to contain M-mode firmware and reset vector is set to this if enabled. Hence, for S-mode payloads like EDK2, only one pflash instance is available for use. This means both code and NV variables of EDK2 will need to use the same pflash. The OS distros keep the EDK2 FW code as readonly. When non-volatile variables also need to share the same pflash, it is not possible to keep it as readonly since variables need write access. To resolve this issue, the code and NV variables need to be separated. But in that case we need an extra flash. Hence, modify the convention for non-KVM guests such that, pflash0 will contain the M-mode FW only when "-bios none" option is used. Otherwise, pflash0 will contain the S-mode payload FW. This enables both pflash instances available for EDK2 use. When KVM is enabled, pflash0 is always assumed to contain the S-mode payload firmware only. Example usage: 1) pflash0 containing M-mode FW qemu-system-riscv64 -bios none -pflash -machine virt or qemu-system-riscv64 -bios none \ -drive file=,if=pflash,format=raw,unit=0 -machine virt 2) pflash0 containing S-mode payload like EDK2 qemu-system-riscv64 -pflash -pflash -machine virt or qemu-system-riscv64 -bios \ -pflash \ -pflash \ -machine virt or qemu-system-riscv64 -bios \ -drive file=,if=pflash,format=raw,unit=0,readonly=on \ -drive file=,if=pflash,format=raw,unit=1 \ -machine virt Signed-off-by: Sunil V L Reported-by: Heinrich Schuchardt Tested-by: Andrea Bolognani Reviewed-by: Alistair Francis Message-Id: <20230601045910.18646-2-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 53 ++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 245c7b97b2..17fdcef223 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1245,7 +1245,7 @@ static void virt_machine_done(Notifier *notifier, void *data) target_ulong firmware_end_addr, kernel_start_addr; const char *firmware_name = riscv_default_firmware_name(&s->soc[0]); uint32_t fdt_load_addr; - uint64_t kernel_entry; + uint64_t kernel_entry = 0; /* * Only direct boot kernel is currently supported for KVM VM, @@ -1266,42 +1266,31 @@ static void virt_machine_done(Notifier *notifier, void *data) firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, start_addr, NULL); - if (drive_get(IF_PFLASH, 0, 1)) { - /* - * S-mode FW like EDK2 will be kept in second plash (unit 1). - * When both kernel, initrd and pflash options are provided in the - * command line, the kernel and initrd will be copied to the fw_cfg - * table and opensbi will jump to the flash address which is the - * entry point of S-mode FW. It is the job of the S-mode FW to load - * the kernel and initrd using fw_cfg table. - * - * If only pflash is given but not -kernel, then it is the job of - * of the S-mode firmware to locate and load the kernel. - * In either case, the next_addr for opensbi will be the flash address. - */ - riscv_setup_firmware_boot(machine); - kernel_entry = virt_memmap[VIRT_FLASH].base + - virt_memmap[VIRT_FLASH].size / 2; - } else if (machine->kernel_filename) { + if (drive_get(IF_PFLASH, 0, 0)) { + if (machine->firmware && !strcmp(machine->firmware, "none") && + !kvm_enabled()) { + /* + * Pflash was supplied but bios is none and not KVM guest, + * let's overwrite the address we jump to after reset to + * the base of the flash. + */ + start_addr = virt_memmap[VIRT_FLASH].base; + } else { + /* + * Pflash was supplied but either KVM guest or bios is not none. + * In this case, base of the flash would contain S-mode payload. + */ + riscv_setup_firmware_boot(machine); + kernel_entry = virt_memmap[VIRT_FLASH].base; + } + } + + if (machine->kernel_filename && !kernel_entry) { kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], firmware_end_addr); kernel_entry = riscv_load_kernel(machine, &s->soc[0], kernel_start_addr, true, NULL); - } else { - /* - * If dynamic firmware is used, it doesn't know where is the next mode - * if kernel argument is not set. - */ - kernel_entry = 0; - } - - if (drive_get(IF_PFLASH, 0, 0)) { - /* - * Pflash was supplied, let's overwrite the address we jump to after - * reset to the base of the flash. - */ - start_addr = virt_memmap[VIRT_FLASH].base; } fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, From 13bdfb8b5487ad5f68529a53bff09e8a57482206 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 1 Jun 2023 10:29:09 +0530 Subject: [PATCH 315/412] riscv/virt: Support using pflash via -blockdev option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, pflash devices can be configured only via -pflash or -drive options. This is the legacy way and the better way is to use -blockdev as in other architectures. libvirt also has moved to use -blockdev method. To support -blockdev option, pflash devices need to be created in instance_init itself. So, update the code to move the virt_flash_create() to instance_init. Also, use standard interfaces to detect whether pflash0 is configured or not. Signed-off-by: Sunil V L Reported-by: Andrea Bolognani Tested-by: Andrea Bolognani Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20230601045910.18646-3-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 17fdcef223..95708d890e 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1246,6 +1246,7 @@ static void virt_machine_done(Notifier *notifier, void *data) const char *firmware_name = riscv_default_firmware_name(&s->soc[0]); uint32_t fdt_load_addr; uint64_t kernel_entry = 0; + BlockBackend *pflash_blk0; /* * Only direct boot kernel is currently supported for KVM VM, @@ -1266,7 +1267,8 @@ static void virt_machine_done(Notifier *notifier, void *data) firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, start_addr, NULL); - if (drive_get(IF_PFLASH, 0, 0)) { + pflash_blk0 = pflash_cfi01_get_blk(s->flash[0]); + if (pflash_blk0) { if (machine->firmware && !strcmp(machine->firmware, "none") && !kvm_enabled()) { /* @@ -1499,8 +1501,6 @@ static void virt_machine_init(MachineState *machine) sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); - virt_flash_create(s); - for (i = 0; i < ARRAY_SIZE(s->flash); i++) { /* Map legacy -drive if=pflash to machine properties */ pflash_cfi01_legacy_drive(s->flash[i], @@ -1527,6 +1527,8 @@ static void virt_machine_instance_init(Object *obj) { RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + virt_flash_create(s); + s->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); s->acpi = ON_OFF_AUTO_AUTO; From e158a6520a690b98242b9ecb53d25aa639641547 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 1 Jun 2023 10:29:10 +0530 Subject: [PATCH 316/412] docs/system: riscv: Add pflash usage details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pflash devices can be used in virt machine for different purposes like for ROM code or S-mode FW payload. Add a section in the documentation on how to use pflash devices for different purposes. Signed-off-by: Sunil V L Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20230601045910.18646-4-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- docs/system/riscv/virt.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index 4b16e41d7f..b33f45e5b3 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -53,6 +53,37 @@ with the default OpenSBI firmware image as the -bios. It also supports the recommended RISC-V bootflow: U-Boot SPL (M-mode) loads OpenSBI fw_dynamic firmware and U-Boot proper (S-mode), using the standard -bios functionality. +Using flash devices +------------------- + +By default, the first flash device (pflash0) is expected to contain +S-mode firmware code. It can be configured as read-only, with the +second flash device (pflash1) available to store configuration data. + +For example, booting edk2 looks like + +.. code-block:: bash + + $ qemu-system-riscv64 \ + -blockdev node-name=pflash0,driver=file,read-only=on,filename= \ + -blockdev node-name=pflash1,driver=file,filename= \ + -M virt,pflash0=pflash0,pflash1=pflash1 \ + ... other args .... + +For TCG guests only, it is also possible to boot M-mode firmware from +the first flash device (pflash0) by additionally passing ``-bios +none``, as in + +.. code-block:: bash + + $ qemu-system-riscv64 \ + -bios none \ + -blockdev node-name=pflash0,driver=file,read-only=on,filename= \ + -M virt,pflash0=pflash0 \ + ... other args .... + +Firmware images used for pflash must be exactly 32 MiB in size. + Machine-specific options ------------------------ From b84694defbcc675a42bc35c4eb28cac590a5414b Mon Sep 17 00:00:00 2001 From: Ivan Klokov Date: Mon, 10 Apr 2023 15:44:50 +0300 Subject: [PATCH 317/412] util/log: Add vector registers to log Added QEMU option 'vpu' to log vector extension registers such as gpr\fpu. Signed-off-by: Ivan Klokov Reviewed-by: Alistair Francis Message-Id: <20230410124451.15929-2-ivan.klokov@syntacore.com> Signed-off-by: Alistair Francis --- accel/tcg/cpu-exec.c | 3 +++ include/hw/core/cpu.h | 2 ++ include/qemu/log.h | 1 + util/log.c | 2 ++ 4 files changed, 8 insertions(+) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 42086525d7..ebc1db03d7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -313,6 +313,9 @@ static void log_cpu_exec(target_ulong pc, CPUState *cpu, #if defined(TARGET_I386) flags |= CPU_DUMP_CCOP; #endif + if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) { + flags |= CPU_DUMP_VPU; + } cpu_dump_state(cpu, logfile, flags); qemu_log_unlock(logfile); } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 383456d1b3..d84fbccaab 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -544,11 +544,13 @@ GuestPanicInformation *cpu_get_crash_info(CPUState *cpu); * @CPU_DUMP_CODE: * @CPU_DUMP_FPU: dump FPU register state, not just integer * @CPU_DUMP_CCOP: dump info about TCG QEMU's condition code optimization state + * @CPU_DUMP_VPU: dump VPU registers */ enum CPUDumpFlags { CPU_DUMP_CODE = 0x00010000, CPU_DUMP_FPU = 0x00020000, CPU_DUMP_CCOP = 0x00040000, + CPU_DUMP_VPU = 0x00080000, }; /** diff --git a/include/qemu/log.h b/include/qemu/log.h index c5643d8dd5..df59bfabcd 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -35,6 +35,7 @@ bool qemu_log_separate(void); /* LOG_STRACE is used for user-mode strace logging. */ #define LOG_STRACE (1 << 19) #define LOG_PER_THREAD (1 << 20) +#define CPU_LOG_TB_VPU (1 << 21) /* Lock/unlock output. */ diff --git a/util/log.c b/util/log.c index 53b4f6c58e..def88a9402 100644 --- a/util/log.c +++ b/util/log.c @@ -495,6 +495,8 @@ const QEMULogItem qemu_log_items[] = { "log every user-mode syscall, its input, and its result" }, { LOG_PER_THREAD, "tid", "open a separate log file per thread; filename must contain '%d'" }, + { CPU_LOG_TB_VPU, "vpu", + "include VPU registers in the 'cpu' logging" }, { 0, NULL, NULL }, }; From b83e4f1db44e2b58227bd5b0a2e3d8fd755b2881 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Sat, 10 Jun 2023 17:46:51 +0800 Subject: [PATCH 318/412] target/riscv: Fix initialized value for cur_pmmask We initialize cur_pmmask as -1(UINT32_MAX/UINT64_MAX) and regard it as if pointer mask is disabled in current implementation. However, the addresses for vector load/store will be adjusted to zero in this case and -1(UINT32_MAX/UINT64_MAX) is valid value for pmmask when pointer mask is enabled. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: LIU Zhiwei Message-Id: <20230610094651.43786-1-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 523311b184..90cef9856d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -134,7 +134,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, flags = FIELD_DP32(flags, TB_FLAGS, FS, fs); flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); - if (env->cur_pmmask < (env->xl == MXL_RV32 ? UINT32_MAX : UINT64_MAX)) { + if (env->cur_pmmask != 0) { flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1); } if (env->cur_pmbase != 0) { @@ -146,7 +146,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, void riscv_cpu_update_mask(CPURISCVState *env) { - target_ulong mask = -1, base = 0; + target_ulong mask = 0, base = 0; /* * TODO: Current RVJ spec does not specify * how the extension interacts with XLEN. From fba59e0f46a0a17a5aa0e57439a11b7048d3f2c6 Mon Sep 17 00:00:00 2001 From: Xiao Wang Date: Thu, 8 Jun 2023 13:35:17 +0800 Subject: [PATCH 319/412] target/riscv/vector_helper.c: clean up reference of MTYPE There's no code using MTYPE, which was a concept used in older vector implementation. Signed-off-by: Xiao Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Message-Id: <20230608053517.4102648-1-xiao.w.wang@intel.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7505f9470a..e8af64e626 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -388,7 +388,7 @@ vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, /* * masked unit-stride load and store operation will be a special case of - * stride, stride = NF * sizeof (MTYPE) + * stride, stride = NF * sizeof (ETYPE) */ #define GEN_VEXT_LD_US(NAME, ETYPE, LOAD_FN) \ @@ -660,10 +660,6 @@ GEN_VEXT_LDFF(vle64ff_v, int64_t, lde_d) #define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) #define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) -/* Unsigned min/max */ -#define DO_MAXU(N, M) DO_MAX((UMTYPE)N, (UMTYPE)M) -#define DO_MINU(N, M) DO_MIN((UMTYPE)N, (UMTYPE)M) - /* * load and store whole register instructions */ From 949b6bcb27295eb04350afac32a45b698fc50104 Mon Sep 17 00:00:00 2001 From: Xiao Wang Date: Wed, 7 Jun 2023 17:16:46 +0800 Subject: [PATCH 320/412] target/riscv/vector_helper.c: Remove the check for extra tail elements Commit 752614cab8e6 ("target/riscv: rvv: Add tail agnostic for vector load / store instructions") added an extra check for LMUL fragmentation, intended for setting the "rest tail elements" in the last register for a segment load insn. Actually, the max_elements derived in vext_ld*() won't be a fraction of vector register size, since the lmul encoded in desc is emul, which has already been adjusted to 1 for LMUL fragmentation case by vext_get_emul() in trans_rvv.c.inc, for ld_stride(), ld_us(), ld_index() and ldff(). Besides, vext_get_emul() has also taken EEW/SEW into consideration, so no need to call vext_get_total_elems() which would base on the emul to derive another emul, the second emul would be incorrect when esz differs from sew. Thus this patch removes the check for extra tail elements. Fixes: 752614cab8e6 ("target/riscv: rvv: Add tail agnostic for vector load / store instructions") Signed-off-by: Xiao Wang Reviewed-by: Daniel Henrique Barboza Reviewed-by: Weiwei Li Message-Id: <20230607091646.4049428-1-xiao.w.wang@intel.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index e8af64e626..1e06e7447c 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -264,11 +264,10 @@ GEN_VEXT_ST_ELEM(ste_h, int16_t, H2, stw) GEN_VEXT_ST_ELEM(ste_w, int32_t, H4, stl) GEN_VEXT_ST_ELEM(ste_d, int64_t, H8, stq) -static void vext_set_tail_elems_1s(CPURISCVState *env, target_ulong vl, - void *vd, uint32_t desc, uint32_t nf, +static void vext_set_tail_elems_1s(target_ulong vl, void *vd, + uint32_t desc, uint32_t nf, uint32_t esz, uint32_t max_elems) { - uint32_t total_elems, vlenb, registers_used; uint32_t vta = vext_vta(desc); int k; @@ -276,19 +275,10 @@ static void vext_set_tail_elems_1s(CPURISCVState *env, target_ulong vl, return; } - total_elems = vext_get_total_elems(env, desc, esz); - vlenb = riscv_cpu_cfg(env)->vlen >> 3; - for (k = 0; k < nf; ++k) { vext_set_elems_1s(vd, vta, (k * max_elems + vl) * esz, (k * max_elems + max_elems) * esz); } - - if (nf * max_elems % total_elems != 0) { - registers_used = ((nf * max_elems) * esz + (vlenb - 1)) / vlenb; - vext_set_elems_1s(vd, vta, (nf * max_elems) * esz, - registers_used * vlenb); - } } /* @@ -324,7 +314,7 @@ vext_ldst_stride(void *vd, void *v0, target_ulong base, } env->vstart = 0; - vext_set_tail_elems_1s(env, env->vl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_STRIDE(NAME, ETYPE, LOAD_FN) \ @@ -383,7 +373,7 @@ vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, } env->vstart = 0; - vext_set_tail_elems_1s(env, evl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems); } /* @@ -504,7 +494,7 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, } env->vstart = 0; - vext_set_tail_elems_1s(env, env->vl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_INDEX(NAME, ETYPE, INDEX_FN, LOAD_FN) \ @@ -634,7 +624,7 @@ ProbeSuccess: } env->vstart = 0; - vext_set_tail_elems_1s(env, env->vl, vd, desc, nf, esz, max_elems); + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LDFF(NAME, ETYPE, LOAD_FN) \ From a574b27af4fe340684ca9f20560a1b01905e4364 Mon Sep 17 00:00:00 2001 From: Himanshu Chauhan Date: Mon, 5 Jun 2023 22:15:48 +0530 Subject: [PATCH 321/412] target/riscv: Smepmp: Return error when access permission not allowed in PMP On an address match, skip checking for default permissions and return error based on access defined in PMP configuration. v3 Changes: o Removed explicit return of boolean value from comparision of priv/allowed_priv v2 Changes: o Removed goto to return in place when address matches o Call pmp_hart_has_privs_default at the end of the loop Fixes: 90b1fafce06 ("target/riscv: Smepmp: Skip applying default rules when address matches") Signed-off-by: Himanshu Chauhan Reviewed-by: Daniel Henrique Barboza Reviewed-by: Weiwei Li Message-Id: <20230605164548.715336-1-hchauhan@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 418738afd8..9d8db493e6 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -291,7 +291,6 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, pmp_priv_t *allowed_privs, target_ulong mode) { int i = 0; - bool ret = false; int pmp_size = 0; target_ulong s = 0; target_ulong e = 0; @@ -435,17 +434,12 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, * defined with PMP must be used. We shouldn't fallback on * finding default privileges. */ - ret = true; - break; + return (privs & *allowed_privs) == privs; } } /* No rule matched */ - if (!ret) { - ret = pmp_hart_has_privs_default(env, privs, allowed_privs, mode); - } - - return ret; + return pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } /* From 7f750efcaa86dad4d0b748f27b82c2c066b5435b Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Wed, 29 Mar 2023 17:00:06 +0200 Subject: [PATCH 322/412] linux-user, bsd-user: Preserve incoming order of environment variables in the target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not reverse the order of environment variables in the target environ array relative to the incoming environ order. Some testsuites depend on a specific order, even though it is not defined by any standard. Signed-off-by: Andreas Schwab Reviewed-by: Warner Losh Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- bsd-user/main.c | 10 +++++++++- linux-user/main.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index cd8b2a670f..b597328118 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -295,8 +295,16 @@ int main(int argc, char **argv) envlist = envlist_create(); - /* add current environment into the list */ + /* + * add current environment into the list + * envlist_setenv adds to the front of the list; to preserve environ + * order add from back to front + */ for (wrk = environ; *wrk != NULL; wrk++) { + continue; + } + while (wrk != environ) { + wrk--; (void) envlist_setenv(envlist, *wrk); } diff --git a/linux-user/main.c b/linux-user/main.c index 5e6b2e1714..dba67ffa36 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -692,8 +692,16 @@ int main(int argc, char **argv, char **envp) envlist = envlist_create(); - /* add current environment into the list */ + /* + * add current environment into the list + * envlist_setenv adds to the front of the list; to preserve environ + * order add from back to front + */ for (wrk = environ; *wrk != NULL; wrk++) { + continue; + } + while (wrk != environ) { + wrk--; (void) envlist_setenv(envlist, *wrk); } From f975033d56dbf945ca7a4247c301c217fa4972f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Apr 2023 18:55:28 +0200 Subject: [PATCH 323/412] cocoa: Fix warnings about invalid prototype declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following Cocoa trivial warnings: C compiler for the host machine: cc (clang 14.0.0 "Apple clang version 14.0.0 (clang-1400.0.29.202)") Objective-C compiler for the host machine: clang (clang 14.0.0) [100/334] Compiling Objective-C object libcommon.fa.p/net_vmnet-bridged.m.o net/vmnet-bridged.m:40:31: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes] static char* get_valid_ifnames() ^ void [742/1436] Compiling Objective-C object libcommon.fa.p/ui_cocoa.m.o ui/cocoa.m:1937:22: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes] static int cocoa_main() ^ void Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki Message-Id: <20230425192820.34063-1-philmd@linaro.org> --- net/vmnet-bridged.m | 2 +- ui/cocoa.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/vmnet-bridged.m b/net/vmnet-bridged.m index 46d2282863..76a28abe79 100644 --- a/net/vmnet-bridged.m +++ b/net/vmnet-bridged.m @@ -37,7 +37,7 @@ done: } -static char* get_valid_ifnames() +static char* get_valid_ifnames(void) { xpc_object_t shared_if_list = vmnet_copy_shared_interface_list(); __block char *if_list = NULL; diff --git a/ui/cocoa.m b/ui/cocoa.m index 168170a8a6..0c2153d17c 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1934,7 +1934,7 @@ static void *call_qemu_main(void *opaque) exit(status); } -static int cocoa_main() +static int cocoa_main(void) { QemuThread thread; From 0baf54d0056979d6344b872a5b20acb6a5fd3cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 5 Jun 2023 16:05:56 +0200 Subject: [PATCH 324/412] util/cacheflush: Use declarations from on Darwin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the cache(3) man page, sys_icache_invalidate() and sys_dcache_flush() are declared in . Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230605175647.88395-2-philmd@linaro.org> --- util/cacheflush.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/cacheflush.c b/util/cacheflush.c index 06c2333a60..de35616718 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -237,8 +237,8 @@ static void __attribute__((constructor)) init_cache_info(void) #ifdef CONFIG_DARWIN /* Apple does not expose CTR_EL0, so we must use system interfaces. */ -extern void sys_icache_invalidate(void *start, size_t len); -extern void sys_dcache_flush(void *start, size_t len); +#include + void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) { sys_dcache_flush((void *)rw, len); From bb6af0fa51cac875e5986aada0de339dcc94eca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 5 Jun 2023 21:52:54 +0200 Subject: [PATCH 325/412] util/cacheflush: Avoid possible redundant dcache flush on Darwin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit describes sys_icache_invalidate() as "equivalent to sys_cache_control(kCacheFunctionPrepareForExecution)", having kCacheFunctionPrepareForExecution defined as: /* Prepare memory for execution. This should be called * after writing machine instructions to memory, before * executing them. It syncs the dcache and icache. [...] */ Since the dcache is also sync'd, we can avoid the sys_dcache_flush() call when both rx/rw pointers are equal. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20230605195911.96033-1-philmd@linaro.org> --- util/cacheflush.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/cacheflush.c b/util/cacheflush.c index de35616718..a08906155a 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -241,7 +241,14 @@ static void __attribute__((constructor)) init_cache_info(void) void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) { - sys_dcache_flush((void *)rw, len); + if (rx == rw) { + /* + * sys_icache_invalidate() syncs the dcache and icache, + * so no need to call sys_dcache_flush(). + */ + } else { + sys_dcache_flush((void *)rw, len); + } sys_icache_invalidate((void *)rx, len); } #else From ed3958910aef1461d99123c78afb1d70b74a83d0 Mon Sep 17 00:00:00 2001 From: Antonio Caggiano Date: Thu, 8 Jun 2023 14:30:14 +0200 Subject: [PATCH 326/412] accel/hvf: Report HV_DENIED error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On MacOS 11 and subsequent versions, in case the resulting binary is not signed with the proper entitlement, handle and report the HV_DENIED error. Signed-off-by: Antonio Caggiano Message-Id: <20230608123014.28715-1-quic_acaggian@quicinc.com> Signed-off-by: Philippe Mathieu-Daudé --- accel/hvf/hvf-all.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 754707dbfb..4920787af6 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -38,6 +38,12 @@ void assert_hvf_ok(hv_return_t ret) case HV_UNSUPPORTED: error_report("Error: HV_UNSUPPORTED"); break; +#if defined(MAC_OS_VERSION_11_0) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 + case HV_DENIED: + error_report("Error: HV_DENIED"); + break; +#endif default: error_report("Unknown Error"); } From 48b9e026791288088d1a9c807c36276c60e6573f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 3 Jun 2023 00:11:54 +0200 Subject: [PATCH 327/412] target/hppa/meson: Only build int_helper.o with system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit int_helper.c only contains system emulation code: remove the #ifdef'ry and move the file to the meson softmmu source set. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230602223016.58647-1-philmd@linaro.org> --- target/hppa/int_helper.c | 3 --- target/hppa/meson.build | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index f599dccfff..d2480b163b 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -25,7 +25,6 @@ #include "hw/core/cpu.h" #include "hw/hppa/hppa_hardware.h" -#ifndef CONFIG_USER_ONLY static void eval_interrupt(HPPACPU *cpu) { CPUState *cs = CPU(cpu); @@ -273,5 +272,3 @@ bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/hppa/meson.build b/target/hppa/meson.build index 81b4b4e617..83b1e0ee7d 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -7,13 +7,13 @@ hppa_ss.add(files( 'fpu_helper.c', 'gdbstub.c', 'helper.c', - 'int_helper.c', 'op_helper.c', 'translate.c', )) hppa_softmmu_ss = ss.source_set() hppa_softmmu_ss.add(files( + 'int_helper.c', 'machine.c', 'mem_helper.c', 'sys_helper.c', From 3b8484c5d20a06944ebb3335c2f5e8014a5bf855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 3 Jun 2023 00:11:03 +0200 Subject: [PATCH 328/412] target/i386/helper: Remove do_cpu_sipi() stub for user-mode emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 604664726f ("target/i386: Restrict cpu_exec_interrupt() handler to sysemu"), do_cpu_sipi() isn't called anymore on user emulation. Remove the now pointless stub. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230602224628.59546-2-philmd@linaro.org> --- target/i386/cpu.h | 3 ++- target/i386/helper.c | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7201a71de8..cd047e0410 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2285,7 +2285,6 @@ static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc, } void do_cpu_init(X86CPU *cpu); -void do_cpu_sipi(X86CPU *cpu); #define MCE_INJECT_BROADCAST 1 #define MCE_INJECT_UNCOND_AO 2 @@ -2419,6 +2418,8 @@ void x86_cpu_set_default_version(X86CPUVersion version); #ifndef CONFIG_USER_ONLY +void do_cpu_sipi(X86CPU *cpu); + #define APIC_DEFAULT_ADDRESS 0xfee00000 #define APIC_SPACE_SIZE 0x100000 diff --git a/target/i386/helper.c b/target/i386/helper.c index 36bf2107e7..792c8eb45e 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -611,9 +611,6 @@ void do_cpu_sipi(X86CPU *cpu) void do_cpu_init(X86CPU *cpu) { } -void do_cpu_sipi(X86CPU *cpu) -{ -} #endif #ifndef CONFIG_USER_ONLY From 6d70b36b0207120461e586bf25d4e47baff1a0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 3 Jun 2023 00:31:40 +0200 Subject: [PATCH 329/412] target/i386/helper: Shuffle do_cpu_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the #ifdef'ry inside do_cpu_init() instead of declaring an empty stub for user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230602224628.59546-3-philmd@linaro.org> --- target/i386/helper.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/target/i386/helper.c b/target/i386/helper.c index 792c8eb45e..89aa696c6d 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -580,9 +580,9 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, return 1; } -#if !defined(CONFIG_USER_ONLY) void do_cpu_init(X86CPU *cpu) { +#if !defined(CONFIG_USER_ONLY) CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; CPUX86State *save = g_new(CPUX86State, 1); @@ -601,19 +601,15 @@ void do_cpu_init(X86CPU *cpu) kvm_arch_do_init_vcpu(cpu); } apic_init_reset(cpu->apic_state); +#endif /* CONFIG_USER_ONLY */ } +#ifndef CONFIG_USER_ONLY + void do_cpu_sipi(X86CPU *cpu) { apic_sipi(cpu->apic_state); } -#else -void do_cpu_init(X86CPU *cpu) -{ -} -#endif - -#ifndef CONFIG_USER_ONLY void cpu_load_efer(CPUX86State *env, uint64_t val) { From f1cc7c28b611a5521e7e2e90d562e4b25af97d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 6 Jun 2023 15:29:54 +0200 Subject: [PATCH 330/412] target/i386: Rename helper template headers as '.h.inc' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented as the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore move the included templates in the tcg/ directory and rename as '.h.inc'. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230608133108.72655-5-philmd@linaro.org> --- target/i386/helper.h | 6 +++--- target/i386/tcg/cc_helper.c | 8 ++++---- .../{cc_helper_template.h => cc_helper_template.h.inc} | 0 target/i386/tcg/int_helper.c | 8 ++++---- .../i386/{ops_sse_header.h => tcg/ops_sse_header.h.inc} | 0 .../shift_helper_template.h.inc} | 0 6 files changed, 11 insertions(+), 11 deletions(-) rename target/i386/tcg/{cc_helper_template.h => cc_helper_template.h.inc} (100%) rename target/i386/{ops_sse_header.h => tcg/ops_sse_header.h.inc} (100%) rename target/i386/{shift_helper_template.h => tcg/shift_helper_template.h.inc} (100%) diff --git a/target/i386/helper.h b/target/i386/helper.h index e627a93107..48609c210b 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -203,11 +203,11 @@ DEF_HELPER_1(enter_mmx, void, env) DEF_HELPER_1(emms, void, env) #define SHIFT 0 -#include "ops_sse_header.h" +#include "tcg/ops_sse_header.h.inc" #define SHIFT 1 -#include "ops_sse_header.h" +#include "tcg/ops_sse_header.h.inc" #define SHIFT 2 -#include "ops_sse_header.h" +#include "tcg/ops_sse_header.h.inc" DEF_HELPER_3(rclb, tl, env, tl, tl) DEF_HELPER_3(rclw, tl, env, tl, tl) diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c index 6227dbb30b..c310bd842f 100644 --- a/target/i386/tcg/cc_helper.c +++ b/target/i386/tcg/cc_helper.c @@ -58,21 +58,21 @@ const uint8_t parity_table[256] = { }; #define SHIFT 0 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #define SHIFT 1 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #define SHIFT 2 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #ifdef TARGET_X86_64 #define SHIFT 3 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #endif diff --git a/target/i386/tcg/cc_helper_template.h b/target/i386/tcg/cc_helper_template.h.inc similarity index 100% rename from target/i386/tcg/cc_helper_template.h rename to target/i386/tcg/cc_helper_template.h.inc diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index 599ac968b0..05418f181f 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -448,20 +448,20 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) } #define SHIFT 0 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #define SHIFT 1 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #define SHIFT 2 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #ifdef TARGET_X86_64 #define SHIFT 3 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #endif diff --git a/target/i386/ops_sse_header.h b/target/i386/tcg/ops_sse_header.h.inc similarity index 100% rename from target/i386/ops_sse_header.h rename to target/i386/tcg/ops_sse_header.h.inc diff --git a/target/i386/shift_helper_template.h b/target/i386/tcg/shift_helper_template.h.inc similarity index 100% rename from target/i386/shift_helper_template.h rename to target/i386/tcg/shift_helper_template.h.inc From 29770e09e4c877fa9cc6cedb93c5bcbf9ee10b50 Mon Sep 17 00:00:00 2001 From: Patrick Venture Date: Wed, 22 Mar 2023 10:21:36 -0700 Subject: [PATCH 331/412] hw/i2c: Enable an id for the pca954x devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the devices to be more readily found and specified. Without setting the name field, they can only be found by device type name, which doesn't let you specify the second of the same device type behind a bus. Tested: Verified that by default the device was findable with the name 'pca954x[77]', for an instance attached at that address. Signed-off-by: Patrick Venture Reviewed-by: Hao Wu Reviewed-by: Philippe Mathieu-Daudé Acked-by: Corey Minyard Message-Id: <20230322172136.48010-1-venture@google.com> [PMD: Fix typo in property name] Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/i2c_mux_pca954x.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index 3945de795c..db5db956a6 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -20,6 +20,7 @@ #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_slave.h" #include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qemu/log.h" #include "qemu/module.h" @@ -43,6 +44,8 @@ typedef struct Pca954xState { bool enabled[PCA9548_CHANNEL_COUNT]; I2CBus *bus[PCA9548_CHANNEL_COUNT]; + + char *name; } Pca954xState; /* @@ -181,6 +184,17 @@ static void pca9548_class_init(ObjectClass *klass, void *data) s->nchans = PCA9548_CHANNEL_COUNT; } +static void pca954x_realize(DeviceState *dev, Error **errp) +{ + Pca954xState *s = PCA954X(dev); + DeviceState *d = DEVICE(s); + if (s->name) { + d->id = g_strdup(s->name); + } else { + d->id = g_strdup_printf("pca954x[%x]", s->parent.i2c.address); + } +} + static void pca954x_init(Object *obj) { Pca954xState *s = PCA954X(obj); @@ -197,6 +211,11 @@ static void pca954x_init(Object *obj) } } +static Property pca954x_props[] = { + DEFINE_PROP_STRING("name", Pca954xState, name), + DEFINE_PROP_END_OF_LIST() +}; + static void pca954x_class_init(ObjectClass *klass, void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); @@ -209,9 +228,12 @@ static void pca954x_class_init(ObjectClass *klass, void *data) rc->phases.enter = pca954x_enter_reset; dc->desc = "Pca954x i2c-mux"; + dc->realize = pca954x_realize; k->write_data = pca954x_write_data; k->receive_byte = pca954x_read_byte; + + device_class_set_props(dc, pca954x_props); } static const TypeInfo pca954x_info[] = { From af33a321fabf9ae85781abe425acbfcbbbd99f9e Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 1 Jun 2023 15:44:27 +0200 Subject: [PATCH 332/412] hw/ide/ahci: Remove stray backslash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This backslash obviously does not belong here, so remove it. Signed-off-by: Niklas Cassel Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: John Snow Message-Id: <20230601134434.519805-2-nks@flawful.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/ahci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 4e76d6b191..48d550f633 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -690,7 +690,7 @@ static void ahci_reset_port(AHCIState *s, int port) s->dev[port].port_state = STATE_RUN; if (ide_state->drive_kind == IDE_CD) { - ahci_set_signature(d, SATA_SIGNATURE_CDROM);\ + ahci_set_signature(d, SATA_SIGNATURE_CDROM); ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; } else { ahci_set_signature(d, SATA_SIGNATURE_DISK); From 48143e0fd2a57371e939750c14f9dd622f5c73a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Dec 2019 18:34:00 +0100 Subject: [PATCH 333/412] hw/scsi/megasas: Silent GCC duplicated-cond warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC9 is confused when building with CFLAG -O3: hw/scsi/megasas.c: In function ‘megasas_scsi_realize’: hw/scsi/megasas.c:2387:26: error: duplicated ‘if’ condition [-Werror=duplicated-cond] 2387 | } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) { hw/scsi/megasas.c:2385:19: note: previously used here 2385 | if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) { cc1: all warnings being treated as errors When this device was introduced in commit e8f943c3bcc, the author cared about modularity, using a definition for the firmware limit. However if the firmware limit isn't changed (MEGASAS_MAX_SGE = 128), the code ends doing the same check twice. Per the maintainer [*]: > The original code assumed that one could change MFI_PASS_FRAME_SIZE, > but it turned out not to be possible as it's being hardcoded in the > drivers themselves (even though the interface provides mechanisms to > query it). So we can remove the duplicate lines. Add the 'MEGASAS_MIN_SGE' definition for the '64' magic value, slightly rewrite the condition check to simplify a bit the logic and remove the unnecessary / duplicated check. [*] https://lore.kernel.org/qemu-devel/e0029fc5-882f-1d63-15e3-1c3dbe9b6a2c@suse.de/ Suggested-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Hannes Reinecke Message-Id: <20230328210126.16282-1-philmd@linaro.org> --- hw/scsi/megasas.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 9cbbb16121..32c70c9e99 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -42,6 +42,7 @@ #define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ #define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */ #define MEGASAS_GEN2_DEFAULT_FRAMES 1008 /* Windows requires this */ +#define MEGASAS_MIN_SGE 64 #define MEGASAS_MAX_SGE 128 /* Firmware limit */ #define MEGASAS_DEFAULT_SGE 80 #define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ @@ -2356,6 +2357,7 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) MegasasState *s = MEGASAS(dev); MegasasBaseClass *b = MEGASAS_GET_CLASS(s); uint8_t *pci_conf; + uint32_t sge; int i, bar_type; Error *err = NULL; int ret; @@ -2424,13 +2426,15 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) if (!s->hba_serial) { s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL); } - if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) { - s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE; - } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) { - s->fw_sge = 128 - MFI_PASS_FRAME_SIZE; - } else { - s->fw_sge = 64 - MFI_PASS_FRAME_SIZE; + + sge = s->fw_sge + MFI_PASS_FRAME_SIZE; + if (sge < MEGASAS_MIN_SGE) { + sge = MEGASAS_MIN_SGE; + } else if (sge >= MEGASAS_MAX_SGE) { + sge = MEGASAS_MAX_SGE; } + s->fw_sge = sge - MFI_PASS_FRAME_SIZE; + if (s->fw_cmds > MEGASAS_MAX_FRAMES) { s->fw_cmds = MEGASAS_MAX_FRAMES; } From 8ff98e09f3ad447255368eccaac7412dd15e8777 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 12 Jun 2023 10:12:37 +0200 Subject: [PATCH 334/412] hw/char/parallel: Export struct ParallelState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exporting ParallelState is a precondition for exporing TYPE_ISA_PARALLEL to be performed in the next patch. Suggested-by: Mark Cave-Ayland Signed-off-by: Bernhard Beschow Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230612081238.1742-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/parallel.c | 20 -------------------- include/hw/char/parallel.h | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 3d32589bb3..e75fc5019d 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -27,10 +27,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "chardev/char-parallel.h" -#include "chardev/char-fe.h" #include "hw/acpi/acpi_aml_interface.h" -#include "hw/irq.h" -#include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" @@ -76,23 +73,6 @@ #define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE) -typedef struct ParallelState { - MemoryRegion iomem; - uint8_t dataw; - uint8_t datar; - uint8_t status; - uint8_t control; - qemu_irq irq; - int irq_pending; - CharBackend chr; - int hw_driver; - int epp_timeout; - uint32_t last_read_offset; /* For debugging */ - /* Memory-mapped interface */ - int it_shift; - PortioList portio_list; -} ParallelState; - OBJECT_DECLARE_SIMPLE_TYPE(ISAParallelState, ISA_PARALLEL) struct ISAParallelState { diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h index 29d2876d00..9f76edca81 100644 --- a/include/hw/char/parallel.h +++ b/include/hw/char/parallel.h @@ -1,9 +1,30 @@ #ifndef HW_PARALLEL_H #define HW_PARALLEL_H +#include "exec/ioport.h" +#include "exec/memory.h" #include "hw/isa/isa.h" +#include "hw/irq.h" +#include "chardev/char-fe.h" #include "chardev/char.h" +typedef struct ParallelState { + MemoryRegion iomem; + uint8_t dataw; + uint8_t datar; + uint8_t status; + uint8_t control; + qemu_irq irq; + int irq_pending; + CharBackend chr; + int hw_driver; + int epp_timeout; + uint32_t last_read_offset; /* For debugging */ + /* Memory-mapped interface */ + int it_shift; + PortioList portio_list; +} ParallelState; + #define TYPE_ISA_PARALLEL "isa-parallel" void parallel_hds_isa_init(ISABus *bus, int n); From 9cc44d9bd6fb8ceb75f8ae898a0d167f6305e511 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 12 Jun 2023 10:12:38 +0200 Subject: [PATCH 335/412] hw/char/parallel-isa: Export struct ISAParallelState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows the struct to be embedded directly into device models without additional allocation. Suggested-by: Mark Cave-Ayland Signed-off-by: Bernhard Beschow Reviewed-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230612081238.1742-3-shentey@gmail.com> [PMD: Update MAINTAINERS entry and use SPDX license identifier] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- hw/char/parallel-isa.c | 1 + hw/char/parallel.c | 12 +----------- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/isa/isa-superio.c | 1 + hw/sparc64/sun4u.c | 2 +- include/hw/char/parallel-isa.h | 30 ++++++++++++++++++++++++++++++ include/hw/char/parallel.h | 2 -- 9 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 include/hw/char/parallel-isa.h diff --git a/MAINTAINERS b/MAINTAINERS index 4a80a38511..88b5a7ee0a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1740,7 +1740,7 @@ F: hw/rtc/mc146818rtc* F: hw/watchdog/wdt_ib700.c F: hw/watchdog/wdt_i6300esb.c F: include/hw/display/vga.h -F: include/hw/char/parallel.h +F: include/hw/char/parallel*.h F: include/hw/dma/i8257.h F: include/hw/i2c/pm_smbus.h F: include/hw/input/i8042.h diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c index 547ae69304..ab0f879998 100644 --- a/hw/char/parallel-isa.c +++ b/hw/char/parallel-isa.c @@ -13,6 +13,7 @@ #include "sysemu/sysemu.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" +#include "hw/char/parallel-isa.h" #include "hw/char/parallel.h" #include "qapi/error.h" diff --git a/hw/char/parallel.c b/hw/char/parallel.c index e75fc5019d..147c900f0d 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -31,6 +31,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" +#include "hw/char/parallel-isa.h" #include "hw/char/parallel.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" @@ -73,17 +74,6 @@ #define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE) -OBJECT_DECLARE_SIMPLE_TYPE(ISAParallelState, ISA_PARALLEL) - -struct ISAParallelState { - ISADevice parent_obj; - - uint32_t index; - uint32_t iobase; - uint32_t isairq; - ParallelState state; -}; - static void parallel_update_irq(ParallelState *s) { if (s->irq_pending) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 42af03dbb4..44146e6ff5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -26,7 +26,7 @@ #include CONFIG_DEVICES #include "qemu/units.h" -#include "hw/char/parallel.h" +#include "hw/char/parallel-isa.h" #include "hw/dma/i8257.h" #include "hw/loader.h" #include "hw/i386/x86.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6155427e48..a9a59ed42b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -30,7 +30,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/char/parallel.h" +#include "hw/char/parallel-isa.h" #include "hw/loader.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/rtc/mc146818rtc.h" diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 9292ec3bcf..7dbfc374da 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -21,6 +21,7 @@ #include "hw/isa/superio.h" #include "hw/qdev-properties.h" #include "hw/input/i8042.h" +#include "hw/char/parallel-isa.h" #include "hw/char/serial.h" #include "trace.h" diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index e2858a0331..29e9b6cc26 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -35,7 +35,7 @@ #include "hw/qdev-properties.h" #include "hw/pci-host/sabre.h" #include "hw/char/serial.h" -#include "hw/char/parallel.h" +#include "hw/char/parallel-isa.h" #include "hw/rtc/m48t59.h" #include "migration/vmstate.h" #include "hw/input/i8042.h" diff --git a/include/hw/char/parallel-isa.h b/include/hw/char/parallel-isa.h new file mode 100644 index 0000000000..d24ccecf05 --- /dev/null +++ b/include/hw/char/parallel-isa.h @@ -0,0 +1,30 @@ +/* + * QEMU ISA Parallel PORT emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2007 Marko Kohtala + * + * SPDX-License-Identifier: MIT + */ + +#ifndef HW_PARALLEL_ISA_H +#define HW_PARALLEL_ISA_H + +#include "parallel.h" + +#include "hw/isa/isa.h" +#include "qom/object.h" + +#define TYPE_ISA_PARALLEL "isa-parallel" +OBJECT_DECLARE_SIMPLE_TYPE(ISAParallelState, ISA_PARALLEL) + +struct ISAParallelState { + ISADevice parent_obj; + + uint32_t index; + uint32_t iobase; + uint32_t isairq; + ParallelState state; +}; + +#endif /* HW_PARALLEL_ISA_H */ diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h index 9f76edca81..7b5a309a03 100644 --- a/include/hw/char/parallel.h +++ b/include/hw/char/parallel.h @@ -25,8 +25,6 @@ typedef struct ParallelState { PortioList portio_list; } ParallelState; -#define TYPE_ISA_PARALLEL "isa-parallel" - void parallel_hds_isa_init(ISABus *bus, int n); bool parallel_mm_init(MemoryRegion *address_space, From f80929f3af3a67a8111fbd9fc73705d4814bcf85 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 30 May 2023 19:05:55 +0100 Subject: [PATCH 336/412] exec/ram_addr: Return number of dirty pages in cpu_physical_memory_set_dirty_lebitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for including the number of dirty pages in the vfio_get_dirty_bitmap() tracepoint, return the number of dirty pages in cpu_physical_memory_set_dirty_lebitmap() similar to cpu_physical_memory_sync_dirty_bitmap(). To avoid counting twice when GLOBAL_DIRTY_RATE is enabled, stash the number of bits set per bitmap quad in a variable (@nbits) and reuse it there. Signed-off-by: Joao Martins Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230530180556.24441-2-joao.m.martins@oracle.com> Signed-off-by: Philippe Mathieu-Daudé --- include/exec/ram_addr.h | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 90a8269290..9f2e3893f5 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -334,14 +334,23 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, } #if !defined(_WIN32) -static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, - ram_addr_t start, - ram_addr_t pages) + +/* + * Contrary to cpu_physical_memory_sync_dirty_bitmap() this function returns + * the number of dirty pages in @bitmap passed as argument. On the other hand, + * cpu_physical_memory_sync_dirty_bitmap() returns newly dirtied pages that + * weren't set in the global migration bitmap. + */ +static inline +uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, + ram_addr_t start, + ram_addr_t pages) { unsigned long i, j; - unsigned long page_number, c; + unsigned long page_number, c, nbits; hwaddr addr; ram_addr_t ram_addr; + uint64_t num_dirty = 0; unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; unsigned long hpratio = qemu_real_host_page_size() / TARGET_PAGE_SIZE; unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); @@ -369,6 +378,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, if (bitmap[k]) { unsigned long temp = leul_to_cpu(bitmap[k]); + nbits = ctpopl(temp); qatomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); if (global_dirty_tracking) { @@ -377,10 +387,12 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, temp); if (unlikely( global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { - total_dirty_pages += ctpopl(temp); + total_dirty_pages += nbits; } } + num_dirty += nbits; + if (tcg_enabled()) { qatomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp); @@ -409,9 +421,11 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, for (i = 0; i < len; i++) { if (bitmap[i] != 0) { c = leul_to_cpu(bitmap[i]); + nbits = ctpopl(c); if (unlikely(global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { - total_dirty_pages += ctpopl(c); + total_dirty_pages += nbits; } + num_dirty += nbits; do { j = ctzl(c); c &= ~(1ul << j); @@ -424,6 +438,8 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } } } + + return num_dirty; } #endif /* not _WIN32 */ From 6fe4f6c941923608027e10af5dd30f18d481f9b9 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 30 May 2023 19:05:56 +0100 Subject: [PATCH 337/412] hw/vfio: Add number of dirty pages to vfio_get_dirty_bitmap tracepoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include the number of dirty pages on the vfio_get_dirty_bitmap tracepoint. These are fetched from the newly added return value in cpu_physical_memory_set_dirty_lebitmap(). Signed-off-by: Joao Martins Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Acked-by: Alex Williamson Message-Id: <20230530180556.24441-3-joao.m.martins@oracle.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/vfio/common.c | 7 ++++--- hw/vfio/trace-events | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 78358ede27..fa8fd949b1 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1747,6 +1747,7 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, { bool all_device_dirty_tracking = vfio_devices_all_device_dirty_tracking(container); + uint64_t dirty_pages; VFIOBitmap vbmap; int ret; @@ -1772,11 +1773,11 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, goto out; } - cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, - vbmap.pages); + dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, + vbmap.pages); trace_vfio_get_dirty_bitmap(container->fd, iova, size, vbmap.size, - ram_addr); + ram_addr, dirty_pages); out: g_free(vbmap.bitmap); diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 646e42fd27..cfb60c354d 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -120,7 +120,7 @@ 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/%08x" vfio_dma_unmap_overflow_workaround(void) "" -vfio_get_dirty_bitmap(int fd, uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start) "container fd=%d, iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64 +vfio_get_dirty_bitmap(int fd, uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "container fd=%d, iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 # platform.c From b0182e537e5aba38031a5009cb16d5e924342458 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 7 Jun 2023 08:18:36 -0700 Subject: [PATCH 338/412] exec/memory: Introduce RAM_NAMED_FILE flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit migrate_ignore_shared() is an optimization that avoids copying memory that is visible and can be mapped on the target. However, a memory-backend-ram or a memory-backend-memfd block with the RAM_SHARED flag set is not migrated when migrate_ignore_shared() is true. This is wrong, because the block has no named backing store, and its contents will be lost. To fix, ignore shared memory iff it is a named file. Define a new flag RAM_NAMED_FILE to distinguish this case. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Message-Id: <1686151116-253260-1-git-send-email-steven.sistare@oracle.com> Signed-off-by: Philippe Mathieu-Daudé --- backends/hostmem-file.c | 1 + include/exec/cpu-common.h | 1 + include/exec/memory.h | 3 +++ migration/ram.c | 3 ++- qapi/migration.json | 4 ++-- softmmu/physmem.c | 7 ++++++- 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 38ea65bec5..b4335a80e6 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -57,6 +57,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; ram_flags |= fb->is_pmem ? RAM_PMEM : 0; + ram_flags |= RAM_NAMED_FILE; memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, backend->size, fb->align, ram_flags, fb->mem_path, fb->offset, fb->readonly, diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index e5a55ede5f..87dc9a752c 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -93,6 +93,7 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb); bool qemu_ram_is_migratable(RAMBlock *rb); void qemu_ram_set_migratable(RAMBlock *rb); void qemu_ram_unset_migratable(RAMBlock *rb); +bool qemu_ram_is_named_file(RAMBlock *rb); int qemu_ram_get_fd(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); diff --git a/include/exec/memory.h b/include/exec/memory.h index c3661b2276..47c2e0221c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -232,6 +232,9 @@ typedef struct IOMMUTLBEvent { /* RAM that isn't accessible through normal means. */ #define RAM_PROTECTED (1 << 8) +/* RAM is an mmap-ed named file */ +#define RAM_NAMED_FILE (1 << 9) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, diff --git a/migration/ram.c b/migration/ram.c index 88a6c82e63..5283a75f02 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -197,7 +197,8 @@ static bool postcopy_preempt_active(void) bool ramblock_is_ignored(RAMBlock *block) { return !qemu_ram_is_migratable(block) || - (migrate_ignore_shared() && qemu_ram_is_shared(block)); + (migrate_ignore_shared() && qemu_ram_is_shared(block) + && qemu_ram_is_named_file(block)); } #undef RAMBLOCK_FOREACH diff --git a/qapi/migration.json b/qapi/migration.json index 179af0c4d8..5bb5ab82a0 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -465,8 +465,8 @@ # block devices (and thus take locks) immediately at the end of # migration. (since 3.0) # -# @x-ignore-shared: If enabled, QEMU will not migrate shared memory -# (since 4.0) +# @x-ignore-shared: If enabled, QEMU will not migrate shared memory that is +# accessible on the destination machine. (since 4.0) # # @validate-uuid: Send the UUID of the source to allow the destination # to ensure it is the same. (since 4.2) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 588d0d166b..6bdd944fe8 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1570,6 +1570,11 @@ void qemu_ram_unset_migratable(RAMBlock *rb) rb->flags &= ~RAM_MIGRATABLE; } +bool qemu_ram_is_named_file(RAMBlock *rb) +{ + return rb->flags & RAM_NAMED_FILE; +} + int qemu_ram_get_fd(RAMBlock *rb) { return rb->fd; @@ -1880,7 +1885,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, /* Just support these ram flags by now. */ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | - RAM_PROTECTED)) == 0); + RAM_PROTECTED | RAM_NAMED_FILE)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); From 860029321d9ebdff47e89561de61e9441fead70a Mon Sep 17 00:00:00 2001 From: Tommy Wu Date: Thu, 8 Jun 2023 22:59:33 -0700 Subject: [PATCH 339/412] hw/intc: If mmsiaddrcfgh.L == 1, smsiaddrcfg and smsiaddrcfgh are read-only. According to the `The RISC-V Advanced Interrupt Architecture` document, if register `mmsiaddrcfgh` of the domain has bit L set to one, then `smsiaddrcfg` and `smsiaddrcfgh` are locked as read-only alongside `mmsiaddrcfg` and `mmsiaddrcfgh`. Signed-off-by: Tommy Wu Reviewed-by: Frank Chang Acked-by: Alistair Francis Reviewed-by: Anup Patel Message-Id: <20230609055936.3925438-1-tommy.wu@sifive.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index afc5b54dbb..4bdc6a5d1a 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -688,13 +688,13 @@ static void riscv_aplic_write(void *opaque, hwaddr addr, uint64_t value, * domains). */ if (aplic->num_children && - !(aplic->smsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + !(aplic->mmsicfgaddrH & APLIC_xMSICFGADDRH_L)) { aplic->smsicfgaddr = value; } } else if (aplic->mmode && aplic->msimode && (addr == APLIC_SMSICFGADDRH)) { if (aplic->num_children && - !(aplic->smsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + !(aplic->mmsicfgaddrH & APLIC_xMSICFGADDRH_L)) { aplic->smsicfgaddrH = value & APLIC_xMSICFGADDRH_VALID_MASK; } } else if ((APLIC_SETIP_BASE <= addr) && From be85508f174def8f13cac3855368a27592a6ea1b Mon Sep 17 00:00:00 2001 From: Ninad Palsule Date: Tue, 23 May 2023 16:45:19 -0500 Subject: [PATCH 340/412] hw/arm/aspeed: Add VPD data for Rainier machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current modeling of Rainier machine creates zero filled VPDs(EEPROMs). This makes some services and applications unhappy and causing them to fail. Hence this drop adds some fabricated data for system and BMC FRU so that vpd services are happy and active. Tested: - The system-vpd.service is active. - VPD service related to bmc is active. Signed-off-by: Ninad Palsule Reviewed-by: Cédric Le Goater [ clg: commit title cleanup ] Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 6 ++++-- hw/arm/aspeed_eeprom.c | 45 +++++++++++++++++++++++++++++++++++++++++- hw/arm/aspeed_eeprom.h | 5 +++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 0b29028fe1..bfc2070bd2 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -788,8 +788,10 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) 0x48); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), TYPE_TMP105, 0x4a); - at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 8), 0x50, 64 * KiB); - at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, 64 * KiB); + at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x50, + 64 * KiB, rainier_bb_fruid, rainier_bb_fruid_len); + at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, + 64 * KiB, rainier_bmc_fruid, rainier_bmc_fruid_len); create_pca9552(soc, 8, 0x60); create_pca9552(soc, 8, 0x61); /* Bus 8: ucd90320@11 */ diff --git a/hw/arm/aspeed_eeprom.c b/hw/arm/aspeed_eeprom.c index dc33a88a54..ace5266cec 100644 --- a/hw/arm/aspeed_eeprom.c +++ b/hw/arm/aspeed_eeprom.c @@ -119,9 +119,52 @@ const uint8_t yosemitev2_bmc_fruid[] = { 0x6e, 0x66, 0x69, 0x67, 0x20, 0x41, 0xc1, 0x45, }; +const uint8_t rainier_bb_fruid[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, + 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02, + 0x01, 0x00, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0x00, 0x00, 0x37, + 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x52, 0x54, + 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x38, 0x56, 0x49, 0x4e, 0x49, + 0x00, 0x00, 0x81, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x53, + 0x59, 0x53, 0x00, 0x00, 0xbb, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x43, 0x45, 0x4e, 0x00, 0x00, 0xe2, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x56, 0x53, 0x42, 0x50, 0x00, 0x00, 0x09, 0x01, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, + 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, 0x49, 0x44, 0x52, 0x04, 0x44, 0x45, + 0x53, 0x43, 0x48, 0x57, 0x02, 0x30, 0x31, 0x43, 0x43, 0x04, 0x33, 0x34, + 0x35, 0x36, 0x46, 0x4e, 0x04, 0x46, 0x52, 0x34, 0x39, 0x53, 0x4e, 0x04, + 0x53, 0x52, 0x31, 0x32, 0x50, 0x4e, 0x04, 0x50, 0x52, 0x39, 0x39, 0x50, + 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x52, 0x54, + 0x04, 0x56, 0x53, 0x59, 0x53, 0x53, 0x45, 0x07, 0x49, 0x42, 0x4d, 0x53, + 0x59, 0x53, 0x31, 0x54, 0x4d, 0x08, 0x32, 0x32, 0x32, 0x32, 0x2d, 0x32, + 0x32, 0x32, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, + 0x00, 0x52, 0x54, 0x04, 0x56, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x07, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x46, 0x43, 0x08, 0x31, 0x31, 0x31, + 0x31, 0x2d, 0x31, 0x31, 0x31, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x52, 0x54, 0x04, 0x56, 0x53, 0x42, 0x50, 0x49, + 0x4d, 0x04, 0x50, 0x00, 0x10, 0x01, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +/* Rainier BMC FRU */ +const uint8_t rainier_bmc_fruid[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, + 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02, + 0x01, 0x00, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0x00, 0x00, 0x37, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x52, 0x54, + 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x0e, 0x56, 0x49, 0x4e, 0x49, + 0x00, 0x00, 0x57, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, + 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, + 0x49, 0x44, 0x52, 0x04, 0x44, 0x45, 0x53, 0x43, 0x48, 0x57, 0x02, 0x30, + 0x31, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + const size_t tiogapass_bmc_fruid_len = sizeof(tiogapass_bmc_fruid); const size_t fby35_nic_fruid_len = sizeof(fby35_nic_fruid); const size_t fby35_bb_fruid_len = sizeof(fby35_bb_fruid); const size_t fby35_bmc_fruid_len = sizeof(fby35_bmc_fruid); - const size_t yosemitev2_bmc_fruid_len = sizeof(yosemitev2_bmc_fruid); +const size_t rainier_bb_fruid_len = sizeof(rainier_bb_fruid); +const size_t rainier_bmc_fruid_len = sizeof(rainier_bmc_fruid); diff --git a/hw/arm/aspeed_eeprom.h b/hw/arm/aspeed_eeprom.h index 86db6f0479..bbf9e54365 100644 --- a/hw/arm/aspeed_eeprom.h +++ b/hw/arm/aspeed_eeprom.h @@ -22,4 +22,9 @@ extern const size_t fby35_bmc_fruid_len; extern const uint8_t yosemitev2_bmc_fruid[]; extern const size_t yosemitev2_bmc_fruid_len; +extern const uint8_t rainier_bb_fruid[]; +extern const size_t rainier_bb_fruid_len; +extern const uint8_t rainier_bmc_fruid[]; +extern const size_t rainier_bmc_fruid_len; + #endif From c8f48b120b31f6bbe33135ef5d478e485c37e3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 7 Jun 2023 06:39:32 +0200 Subject: [PATCH 341/412] aspeed/hace: Initialize g_autofree pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As mentioned in docs/devel/style.rst "Automatic memory deallocation": * Variables declared with g_auto* MUST always be initialized, otherwise the cleanup function will use uninitialized stack memory This avoids QEMU to coredump when running the "hash test" command under Zephyr. Cc: Steven Lee Cc: Joel Stanley Cc: qemu-stable@nongnu.org Fixes: c5475b3f9a ("hw: Model ASPEED's Hash and Crypto Engine") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Reviewed-by: Francisco Iglesias Message-Id: <20230421131547.2177449-1-clg@kaod.org> Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 12a761f1f5..b07506ec04 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -189,7 +189,7 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { struct iovec iov[ASPEED_HACE_MAX_SG]; - g_autofree uint8_t *digest_buf; + g_autofree uint8_t *digest_buf = NULL; size_t digest_len = 0; int niov = 0; int i; From 262259eab15d04900b78b91e307bb8470438a011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 7 Jun 2023 06:39:33 +0200 Subject: [PATCH 342/412] aspeed: Introduce a boot_rom region at the machine level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should also avoid Coverity to report a memory leak warning when the QEMU process exits. See CID 1508061. Reviewed-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index bfc2070bd2..76a1e7303d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -40,6 +40,7 @@ struct AspeedMachineState { /* Public */ AspeedSoCState soc; + MemoryRegion boot_rom; bool mmio_exec; char *fmc_model; char *spi_model; @@ -275,15 +276,15 @@ static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, * Create a ROM and copy the flash contents at the expected address * (0x0). Boots faster than execute-in-place. */ -static void aspeed_install_boot_rom(AspeedSoCState *soc, BlockBackend *blk, +static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, uint64_t rom_size) { - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + AspeedSoCState *soc = &bmc->soc; - memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", rom_size, + memory_region_init_rom(&bmc->boot_rom, NULL, "aspeed.boot_rom", rom_size, &error_abort); memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, - boot_rom, 1); + &bmc->boot_rom, 1); write_boot_rom(blk, ASPEED_SOC_SPI_BOOT_ADDR, rom_size, &error_abort); } @@ -431,8 +432,7 @@ static void aspeed_machine_init(MachineState *machine) if (mtd0) { uint64_t rom_size = memory_region_size(&bmc->soc.spi_boot); - aspeed_install_boot_rom(&bmc->soc, blk_by_legacy_dinfo(mtd0), - rom_size); + aspeed_install_boot_rom(bmc, blk_by_legacy_dinfo(mtd0), rom_size); } } From ebd643ebd2540cd54a6742eaf2e3e4864cf6dd48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 7 Jun 2023 06:39:34 +0200 Subject: [PATCH 343/412] aspeed: Use the boot_rom region of the fby35 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change completes commits 5aa281d757 ("aspeed: Introduce a spi_boot region under the SoC") and 8b744a6a47 ("aspeed: Add a boot_rom overlap region in the SoC spi_boot container") which introduced a spi_boot container at the SoC level to map the boot rom region as an overlap. It also fixes a Coverity report (CID 1508061) for a memory leak warning when the QEMU process exits by using an bmc_boot_rom MemoryRegion available at the machine level. Cc: Peter Delevoryas Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Signed-off-by: Cédric Le Goater --- hw/arm/fby35.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index f4600c290b..f2ff6c1abf 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -70,8 +70,6 @@ static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, static void fby35_bmc_init(Fby35State *s) { - DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); - object_initialize_child(OBJECT(s), "bmc", &s->bmc, "ast2600-a3"); memory_region_init(&s->bmc_memory, OBJECT(&s->bmc), "bmc-memory", @@ -95,18 +93,21 @@ static void fby35_bmc_init(Fby35State *s) aspeed_board_init_flashes(&s->bmc.fmc, "n25q00", 2, 0); /* Install first FMC flash content as a boot rom. */ - if (drive0) { - AspeedSMCFlash *fl = &s->bmc.fmc.flashes[0]; - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); - uint64_t size = memory_region_size(&fl->mmio); + if (!s->mmio_exec) { + DriveInfo *mtd0 = drive_get(IF_MTD, 0, 0); - if (!s->mmio_exec) { - memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", - size, &error_abort); - memory_region_add_subregion(&s->bmc_memory, FBY35_BMC_FIRMWARE_ADDR, - boot_rom); - fby35_bmc_write_boot_rom(drive0, boot_rom, FBY35_BMC_FIRMWARE_ADDR, - size, &error_abort); + if (mtd0) { + AspeedSoCState *bmc = &s->bmc; + uint64_t rom_size = memory_region_size(&bmc->spi_boot); + + memory_region_init_rom(&s->bmc_boot_rom, NULL, "aspeed.boot_rom", + rom_size, &error_abort); + memory_region_add_subregion_overlap(&bmc->spi_boot_container, 0, + &s->bmc_boot_rom, 1); + + fby35_bmc_write_boot_rom(mtd0, &s->bmc_boot_rom, + FBY35_BMC_FIRMWARE_ADDR, + rom_size, &error_abort); } } } From f65f6ad5a749bc2d24a083da3544f47a19e7e81f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 7 Jun 2023 06:39:42 +0200 Subject: [PATCH 344/412] aspeed: Introduce a "bmc-console" machine option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the Aspeed machines use the UART5 device for the boot console, and QEMU connects the first serial Chardev to this SoC device for this purpose. See routine connect_serial_hds_to_uarts(). Nevertheless, some machines use another boot console, such as the fuji, and commit 5d63d0c76c ("hw/arm/aspeed: Allow machine to set UART default") introduced a SoC class attribute 'uart_default' and property to be able to change the boot console device. It was later changed by commit d2b3eaefb4 ("aspeed: Refactor UART init for multi-SoC machines"). The "bmc-console" machine option goes a step further and lets the user define the UART device from the QEMU command line without introducing a new machine definition. For instance, to use device UART3 (mapped on /dev/ttyS2 under Linux) instead of the default UART5, one would use : -M ast2500-evb,bmc-console=uart3 Cc: Abhishek Singh Dagur Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 11 +++++++++++ hw/arm/aspeed.c | 40 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index d4e293e7f9..80538422a1 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -122,6 +122,11 @@ Options specific to Aspeed machines are : * ``spi-model`` to change the SPI Flash model. + * ``bmc-console`` to change the default console device. Most of the + machines use the ``UART5`` device for a boot console, which is + mapped on ``/dev/ttyS4`` under Linux, but it is not always the + case. + For instance, to start the ``ast2500-evb`` machine with a different FMC chip and a bigger (64M) SPI chip, use : @@ -129,6 +134,12 @@ FMC chip and a bigger (64M) SPI chip, use : -M ast2500-evb,fmc-model=mx25l25635e,spi-model=mx66u51235f +To change the boot console and use device ``UART3`` (``/dev/ttyS2`` +under Linux), use : + +.. code-block:: bash + + -M ast2500-evb,bmc-console=uart3 Aspeed minibmc family boards (``ast1030-evb``) ================================================================== diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 76a1e7303d..6880998484 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -42,6 +42,7 @@ struct AspeedMachineState { AspeedSoCState soc; MemoryRegion boot_rom; bool mmio_exec; + uint32_t uart_chosen; char *fmc_model; char *spi_model; }; @@ -333,10 +334,11 @@ static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); AspeedSoCState *s = &bmc->soc; AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; - aspeed_soc_uart_set_chr(s, amc->uart_default, serial_hd(0)); + aspeed_soc_uart_set_chr(s, uart_chosen, serial_hd(0)); for (int i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { - if (uart == amc->uart_default) { + if (uart == uart_chosen) { continue; } aspeed_soc_uart_set_chr(s, uart, serial_hd(i)); @@ -1078,6 +1080,35 @@ static void aspeed_set_spi_model(Object *obj, const char *value, Error **errp) bmc->spi_model = g_strdup(value); } +static char *aspeed_get_bmc_console(Object *obj, Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); + int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; + + return g_strdup_printf("uart%d", uart_chosen - ASPEED_DEV_UART1 + 1); +} + +static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(amc->soc_name)); + int val; + + if (sscanf(value, "uart%u", &val) != 1) { + error_setg(errp, "Bad value for \"uart\" property"); + return; + } + + /* The number of UART depends on the SoC */ + if (val < 1 || val > sc->uarts_num) { + error_setg(errp, "\"uart\" should be in range [1 - %d]", sc->uarts_num); + return; + } + bmc->uart_chosen = ASPEED_DEV_UART1 + val - 1; +} + static void aspeed_machine_class_props_init(ObjectClass *oc) { object_class_property_add_bool(oc, "execute-in-place", @@ -1086,6 +1117,11 @@ static void aspeed_machine_class_props_init(ObjectClass *oc) object_class_property_set_description(oc, "execute-in-place", "boot directly from CE0 flash device"); + object_class_property_add_str(oc, "bmc-console", aspeed_get_bmc_console, + aspeed_set_bmc_console); + object_class_property_set_description(oc, "bmc-console", + "Change the default UART to \"uartX\""); + object_class_property_add_str(oc, "fmc-model", aspeed_get_fmc_model, aspeed_set_fmc_model); object_class_property_set_description(oc, "fmc-model", From 42bea956f6f7477c06186c7add62fa0107a27a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 7 Jun 2023 06:39:43 +0200 Subject: [PATCH 345/412] target/arm: Allow users to set the number of VFP registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cortex A7 CPUs with an FPU implementing VFPv4 without NEON support have 16 64-bit FPU registers and not 32 registers. Let users set the number of VFP registers with a CPU property. The primary use case of this property is for the Cortex A7 of the Aspeed AST2600 SoC. Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Reviewed-by: Peter Maydell Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600.c | 2 ++ target/arm/cpu.c | 32 ++++++++++++++++++++++++++++++++ target/arm/cpu.h | 2 ++ 3 files changed, 36 insertions(+) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 1bf1246148..a8b3a8065a 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -316,6 +316,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_bool(OBJECT(&s->cpu[i]), "neon", false, &error_abort); + object_property_set_bool(OBJECT(&s->cpu[i]), "vfp-d32", false, + &error_abort); object_property_set_link(OBJECT(&s->cpu[i]), "memory", OBJECT(s->memory), &error_abort); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 4d5bb57f07..353fc48567 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1277,6 +1277,9 @@ static Property arm_cpu_cfgend_property = static Property arm_cpu_has_vfp_property = DEFINE_PROP_BOOL("vfp", ARMCPU, has_vfp, true); +static Property arm_cpu_has_vfp_d32_property = + DEFINE_PROP_BOOL("vfp-d32", ARMCPU, has_vfp_d32, true); + static Property arm_cpu_has_neon_property = DEFINE_PROP_BOOL("neon", ARMCPU, has_neon, true); @@ -1408,6 +1411,22 @@ void arm_cpu_post_init(Object *obj) } } + if (cpu->has_vfp && cpu_isar_feature(aa32_simd_r32, cpu)) { + cpu->has_vfp_d32 = true; + if (!kvm_enabled()) { + /* + * The permitted values of the SIMDReg bits [3:0] on + * Armv8-A are either 0b0000 and 0b0010. On such CPUs, + * make sure that has_vfp_d32 can not be set to false. + */ + if (!(arm_feature(&cpu->env, ARM_FEATURE_V8) && + !arm_feature(&cpu->env, ARM_FEATURE_M))) { + qdev_property_add_static(DEVICE(obj), + &arm_cpu_has_vfp_d32_property); + } + } + } + if (arm_feature(&cpu->env, ARM_FEATURE_NEON)) { cpu->has_neon = true; if (!kvm_enabled()) { @@ -1674,6 +1693,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (cpu->has_vfp_d32 != cpu->has_neon) { + error_setg(errp, "ARM CPUs must have both VFP-D32 and Neon or neither"); + return; + } + + if (!cpu->has_vfp_d32) { + uint32_t u; + + u = cpu->isar.mvfr0; + u = FIELD_DP32(u, MVFR0, SIMDREG, 1); /* 16 registers */ + cpu->isar.mvfr0 = u; + } + if (!cpu->has_vfp) { uint64_t t; uint32_t u; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 36c608f0e6..af0119addf 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -924,6 +924,8 @@ struct ArchCPU { bool has_pmu; /* CPU has VFP */ bool has_vfp; + /* CPU has 32 VFP registers */ + bool has_vfp_d32; /* CPU has Neon */ bool has_neon; /* CPU has M-profile DSP extension */ From e7218dd57cb60cb03310a989149963be8c1da4fe Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 14 Jun 2023 17:03:29 -0700 Subject: [PATCH 346/412] hw/i386/xen/: move xen-mapcache.c to hw/xen/ xen-mapcache.c contains common functions which can be used for enabling Xen on aarch64 with IOREQ handling. Moving it out from hw/i386/xen to hw/xen to make it accessible for both aarch64 and x86. Signed-off-by: Vikram Garhwal Signed-off-by: Stefano Stabellini Reviewed-by: Paul Durrant --- hw/i386/meson.build | 1 + hw/i386/xen/meson.build | 1 - hw/i386/xen/trace-events | 5 ----- hw/xen/meson.build | 4 ++++ hw/xen/trace-events | 5 +++++ hw/{i386 => }/xen/xen-mapcache.c | 0 6 files changed, 10 insertions(+), 6 deletions(-) rename hw/{i386 => }/xen/xen-mapcache.c (100%) diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 213e2e82b3..cfdbfdcbcb 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -33,5 +33,6 @@ subdir('kvm') subdir('xen') i386_ss.add_all(xenpv_ss) +i386_ss.add_all(xen_ss) hw_arch += {'i386': i386_ss} diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build index 2e64a34e16..3dc4c4f106 100644 --- a/hw/i386/xen/meson.build +++ b/hw/i386/xen/meson.build @@ -1,6 +1,5 @@ i386_ss.add(when: 'CONFIG_XEN', if_true: files( 'xen-hvm.c', - 'xen-mapcache.c', 'xen_apic.c', 'xen_pvdevice.c', )) diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 5d6be61090..a0c89d91c4 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -21,8 +21,3 @@ xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" -# xen-mapcache.c -xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 -xen_remap_bucket(uint64_t index) "index 0x%"PRIx64 -xen_map_cache_return(void* ptr) "%p" - diff --git a/hw/xen/meson.build b/hw/xen/meson.build index 19c6aabc7c..202752e557 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -26,3 +26,7 @@ else endif specific_ss.add_all(when: ['CONFIG_XEN', xen], if_true: xen_specific_ss) + +xen_ss = ss.source_set() + +xen_ss.add(when: 'CONFIG_XEN', if_true: files('xen-mapcache.c')) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 55c9e1df68..f977c7c8c6 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -41,3 +41,8 @@ xs_node_vprintf(char *path, char *value) "%s %s" xs_node_vscanf(char *path, char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" + +# xen-mapcache.c +xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 +xen_remap_bucket(uint64_t index) "index 0x%"PRIx64 +xen_map_cache_return(void* ptr) "%p" diff --git a/hw/i386/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c similarity index 100% rename from hw/i386/xen/xen-mapcache.c rename to hw/xen/xen-mapcache.c From 33087aacfab86c6a30721f6493391472ace0b752 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 14 Jun 2023 17:03:30 -0700 Subject: [PATCH 347/412] hw/i386/xen: rearrange xen_hvm_init_pc In preparation to moving most of xen-hvm code to an arch-neutral location, move non IOREQ references to: - xen_get_vmport_regs_pfn - xen_suspend_notifier - xen_wakeup_notifier - xen_ram_init towards the end of the xen_hvm_init_pc() function. This is done to keep the common ioreq functions in one place which will be moved to new function in next patch in order to make it common to both x86 and aarch64 machines. Signed-off-by: Vikram Garhwal Signed-off-by: Stefano Stabellini Reviewed-by: Paul Durrant --- hw/i386/xen/xen-hvm.c | 49 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index ab8f1b61ee..7a7764240e 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1419,12 +1419,6 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) state->exit.notify = xen_exit_notifier; qemu_add_exit_notifier(&state->exit); - state->suspend.notify = xen_suspend_notifier; - qemu_register_suspend_notifier(&state->suspend); - - state->wakeup.notify = xen_wakeup_notifier; - qemu_register_wakeup_notifier(&state->wakeup); - /* * Register wake-up support in QMP query-current-machine API */ @@ -1435,23 +1429,6 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - rc = xen_get_vmport_regs_pfn(xen_xc, xen_domid, &ioreq_pfn); - if (!rc) { - DPRINTF("shared vmport page at pfn %lx\n", ioreq_pfn); - state->shared_vmport_page = - xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_vmport_page == NULL) { - error_report("map shared vmport IO page returned error %d handle=%p", - errno, xen_xc); - goto err; - } - } else if (rc != -ENOSYS) { - error_report("get vmport regs pfn returned error %d, rc=%d", - errno, rc); - goto err; - } - /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_new0(CPUState *, max_cpus); @@ -1490,7 +1467,6 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) #else xen_map_cache_init(NULL, state); #endif - xen_ram_init(pcms, ms->ram_size, ram_memory); qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); @@ -1511,6 +1487,31 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) QLIST_INIT(&xen_physmap); xen_read_physmap(state); + state->suspend.notify = xen_suspend_notifier; + qemu_register_suspend_notifier(&state->suspend); + + state->wakeup.notify = xen_wakeup_notifier; + qemu_register_wakeup_notifier(&state->wakeup); + + rc = xen_get_vmport_regs_pfn(xen_xc, xen_domid, &ioreq_pfn); + if (!rc) { + DPRINTF("shared vmport page at pfn %lx\n", ioreq_pfn); + state->shared_vmport_page = + xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_vmport_page == NULL) { + error_report("map shared vmport IO page returned error %d handle=%p", + errno, xen_xc); + goto err; + } + } else if (rc != -ENOSYS) { + error_report("get vmport regs pfn returned error %d, rc=%d", + errno, rc); + goto err; + } + + xen_ram_init(pcms, ms->ram_size, ram_memory); + /* Disable ACPI build because Xen handles it */ pcms->acpi_build_enabled = false; From 9269b9d1888c2b17d2e9948abf9f6310b3afbac3 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 14 Jun 2023 17:03:31 -0700 Subject: [PATCH 348/412] hw/i386/xen/xen-hvm: move x86-specific fields out of XenIOState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to moving most of xen-hvm code to an arch-neutral location, move: - shared_vmport_page - log_for_dirtybit - dirty_bitmap - suspend - wakeup out of XenIOState struct as these are only used on x86, especially the ones related to dirty logging. Updated XenIOState can be used for both aarch64 and x86. Also, remove free_phys_offset as it was unused. Signed-off-by: Stefano Stabellini Signed-off-by: Vikram Garhwal Reviewed-by: Paul Durrant Reviewed-by: Alex Bennée --- hw/i386/xen/xen-hvm.c | 58 ++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 7a7764240e..01bf947f1c 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -74,6 +74,7 @@ struct shared_vmport_iopage { }; typedef struct shared_vmport_iopage shared_vmport_iopage_t; #endif +static shared_vmport_iopage_t *shared_vmport_page; static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) { @@ -96,6 +97,11 @@ typedef struct XenPhysmap { } XenPhysmap; static QLIST_HEAD(, XenPhysmap) xen_physmap; +static const XenPhysmap *log_for_dirtybit; +/* Buffer used by xen_sync_dirty_bitmap */ +static unsigned long *dirty_bitmap; +static Notifier suspend; +static Notifier wakeup; typedef struct XenPciDevice { PCIDevice *pci_dev; @@ -106,7 +112,6 @@ typedef struct XenPciDevice { typedef struct XenIOState { ioservid_t ioservid; shared_iopage_t *shared_page; - shared_vmport_iopage_t *shared_vmport_page; buffered_iopage_t *buffered_io_page; xenforeignmemory_resource_handle *fres; QEMUTimer *buffered_io_timer; @@ -126,14 +131,8 @@ typedef struct XenIOState { MemoryListener io_listener; QLIST_HEAD(, XenPciDevice) dev_list; DeviceListener device_listener; - hwaddr free_phys_offset; - const XenPhysmap *log_for_dirtybit; - /* Buffer used by xen_sync_dirty_bitmap */ - unsigned long *dirty_bitmap; Notifier exit; - Notifier suspend; - Notifier wakeup; } XenIOState; /* Xen specific function for piix pci */ @@ -463,10 +462,10 @@ static int xen_remove_from_physmap(XenIOState *state, } QLIST_REMOVE(physmap, list); - if (state->log_for_dirtybit == physmap) { - state->log_for_dirtybit = NULL; - g_free(state->dirty_bitmap); - state->dirty_bitmap = NULL; + if (log_for_dirtybit == physmap) { + log_for_dirtybit = NULL; + g_free(dirty_bitmap); + dirty_bitmap = NULL; } g_free(physmap); @@ -627,16 +626,16 @@ static void xen_sync_dirty_bitmap(XenIOState *state, return; } - if (state->log_for_dirtybit == NULL) { - state->log_for_dirtybit = physmap; - state->dirty_bitmap = g_new(unsigned long, bitmap_size); - } else if (state->log_for_dirtybit != physmap) { + if (log_for_dirtybit == NULL) { + log_for_dirtybit = physmap; + dirty_bitmap = g_new(unsigned long, bitmap_size); + } else if (log_for_dirtybit != physmap) { /* Only one range for dirty bitmap can be tracked. */ return; } rc = xen_track_dirty_vram(xen_domid, start_addr >> TARGET_PAGE_BITS, - npages, state->dirty_bitmap); + npages, dirty_bitmap); if (rc < 0) { #ifndef ENODATA #define ENODATA ENOENT @@ -651,7 +650,7 @@ static void xen_sync_dirty_bitmap(XenIOState *state, } for (i = 0; i < bitmap_size; i++) { - unsigned long map = state->dirty_bitmap[i]; + unsigned long map = dirty_bitmap[i]; while (map != 0) { j = ctzl(map); map &= ~(1ul << j); @@ -677,12 +676,10 @@ static void xen_log_start(MemoryListener *listener, static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, int old, int new) { - XenIOState *state = container_of(listener, XenIOState, memory_listener); - if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { - state->log_for_dirtybit = NULL; - g_free(state->dirty_bitmap); - state->dirty_bitmap = NULL; + log_for_dirtybit = NULL; + g_free(dirty_bitmap); + dirty_bitmap = NULL; /* Disable dirty bit tracking */ xen_track_dirty_vram(xen_domid, 0, 0, NULL); } @@ -1022,9 +1019,9 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) { vmware_regs_t *vmport_regs; - assert(state->shared_vmport_page); + assert(shared_vmport_page); vmport_regs = - &state->shared_vmport_page->vcpu_vmport_regs[state->send_vcpu]; + &shared_vmport_page->vcpu_vmport_regs[state->send_vcpu]; QEMU_BUILD_BUG_ON(sizeof(*req) < sizeof(*vmport_regs)); current_cpu = state->cpu_by_vcpu_id[state->send_vcpu]; @@ -1472,7 +1469,6 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) state->memory_listener = xen_memory_listener; memory_listener_register(&state->memory_listener, &address_space_memory); - state->log_for_dirtybit = NULL; state->io_listener = xen_io_listener; memory_listener_register(&state->io_listener, &address_space_io); @@ -1487,19 +1483,19 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) QLIST_INIT(&xen_physmap); xen_read_physmap(state); - state->suspend.notify = xen_suspend_notifier; - qemu_register_suspend_notifier(&state->suspend); + suspend.notify = xen_suspend_notifier; + qemu_register_suspend_notifier(&suspend); - state->wakeup.notify = xen_wakeup_notifier; - qemu_register_wakeup_notifier(&state->wakeup); + wakeup.notify = xen_wakeup_notifier; + qemu_register_wakeup_notifier(&wakeup); rc = xen_get_vmport_regs_pfn(xen_xc, xen_domid, &ioreq_pfn); if (!rc) { DPRINTF("shared vmport page at pfn %lx\n", ioreq_pfn); - state->shared_vmport_page = + shared_vmport_page = xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, 1, &ioreq_pfn, NULL); - if (state->shared_vmport_page == NULL) { + if (shared_vmport_page == NULL) { error_report("map shared vmport IO page returned error %d handle=%p", errno, xen_xc); goto err; From f17068c1c784d9732982e8977d6f18dce08f1fe1 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 14 Jun 2023 17:03:32 -0700 Subject: [PATCH 349/412] xen-hvm: reorganize xen-hvm and move common function to xen-hvm-common This patch does following: 1. creates arch_handle_ioreq() and arch_xen_set_memory(). This is done in preparation for moving most of xen-hvm code to an arch-neutral location, move the x86-specific portion of xen_set_memory to arch_xen_set_memory. Also, move handle_vmport_ioreq to arch_handle_ioreq. 2. Pure code movement: move common functions to hw/xen/xen-hvm-common.c Extract common functionalities from hw/i386/xen/xen-hvm.c and move them to hw/xen/xen-hvm-common.c. These common functions are useful for creating an IOREQ server. xen_hvm_init_pc() contains the architecture independent code for creating and mapping a IOREQ server, connecting memory and IO listeners, initializing a xen bus and registering backends. Moved this common xen code to a new function xen_register_ioreq() which can be used by both x86 and ARM machines. Following functions are moved to hw/xen/xen-hvm-common.c: xen_vcpu_eport(), xen_vcpu_ioreq(), xen_ram_alloc(), xen_set_memory(), xen_region_add(), xen_region_del(), xen_io_add(), xen_io_del(), xen_device_realize(), xen_device_unrealize(), cpu_get_ioreq_from_shared_memory(), cpu_get_ioreq(), do_inp(), do_outp(), rw_phys_req_item(), read_phys_req_item(), write_phys_req_item(), cpu_ioreq_pio(), cpu_ioreq_move(), cpu_ioreq_config(), handle_ioreq(), handle_buffered_iopage(), handle_buffered_io(), cpu_handle_ioreq(), xen_main_loop_prepare(), xen_hvm_change_state_handler(), xen_exit_notifier(), xen_map_ioreq_server(), destroy_hvm_domain() and xen_shutdown_fatal_error() 3. Removed static type from below functions: 1. xen_region_add() 2. xen_region_del() 3. xen_io_add() 4. xen_io_del() 5. xen_device_realize() 6. xen_device_unrealize() 7. xen_hvm_change_state_handler() 8. cpu_ioreq_pio() 9. xen_exit_notifier() 4. Replace TARGET_PAGE_SIZE with XC_PAGE_SIZE to match the page side with Xen. Signed-off-by: Vikram Garhwal Signed-off-by: Stefano Stabellini Acked-by: Stefano Stabellini --- hw/i386/xen/trace-events | 14 - hw/i386/xen/xen-hvm.c | 1022 ++----------------------------- hw/xen/meson.build | 5 +- hw/xen/trace-events | 14 + hw/xen/xen-hvm-common.c | 860 ++++++++++++++++++++++++++ include/hw/i386/xen_arch_hvm.h | 11 + include/hw/xen/arch_hvm.h | 3 + include/hw/xen/xen-hvm-common.h | 99 +++ 8 files changed, 1057 insertions(+), 971 deletions(-) create mode 100644 hw/xen/xen-hvm-common.c create mode 100644 include/hw/i386/xen_arch_hvm.h create mode 100644 include/hw/xen/arch_hvm.h create mode 100644 include/hw/xen/xen-hvm-common.h diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index a0c89d91c4..5d0a8d6dcf 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -7,17 +7,3 @@ xen_platform_log(char *s) "xen platform: %s" xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address 0x%"PRIx64")" xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address 0x%"PRIx64")" -# xen-hvm.c -xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: 0x%lx, size 0x%lx" -xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "0x%"PRIx64" size 0x%lx, log_dirty %i" -handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" -cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" -cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" -cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" -cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" - diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 01bf947f1c..5dc5e80535 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -10,43 +10,21 @@ #include "qemu/osdep.h" #include "qemu/units.h" - -#include "cpu.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/i386/pc.h" -#include "hw/irq.h" -#include "hw/hw.h" -#include "hw/i386/apic-msidef.h" -#include "hw/xen/xen_native.h" -#include "hw/xen/xen-legacy-backend.h" -#include "hw/xen/xen-bus.h" -#include "hw/xen/xen-x86.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "qemu/range.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" -#include "sysemu/xen-mapcache.h" #include "trace.h" -#include +#include "hw/i386/pc.h" +#include "hw/irq.h" +#include "hw/i386/apic-msidef.h" +#include "hw/xen/xen-x86.h" +#include "qemu/range.h" + +#include "hw/xen/xen-hvm-common.h" +#include "hw/xen/arch_hvm.h" #include -//#define DEBUG_XEN_HVM - -#ifdef DEBUG_XEN_HVM -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -static MemoryRegion ram_memory, ram_640k, ram_lo, ram_hi; +static MemoryRegion ram_640k, ram_lo, ram_hi; static MemoryRegion *framebuffer; static bool xen_in_migration; @@ -74,28 +52,9 @@ struct shared_vmport_iopage { }; typedef struct shared_vmport_iopage shared_vmport_iopage_t; #endif + static shared_vmport_iopage_t *shared_vmport_page; -static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) -{ - return shared_page->vcpu_ioreq[i].vp_eport; -} -static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) -{ - return &shared_page->vcpu_ioreq[vcpu]; -} - -#define BUFFER_IO_MAX_DELAY 100 - -typedef struct XenPhysmap { - hwaddr start_addr; - ram_addr_t size; - const char *name; - hwaddr phys_offset; - - QLIST_ENTRY(XenPhysmap) list; -} XenPhysmap; - static QLIST_HEAD(, XenPhysmap) xen_physmap; static const XenPhysmap *log_for_dirtybit; /* Buffer used by xen_sync_dirty_bitmap */ @@ -103,38 +62,6 @@ static unsigned long *dirty_bitmap; static Notifier suspend; static Notifier wakeup; -typedef struct XenPciDevice { - PCIDevice *pci_dev; - uint32_t sbdf; - QLIST_ENTRY(XenPciDevice) entry; -} XenPciDevice; - -typedef struct XenIOState { - ioservid_t ioservid; - shared_iopage_t *shared_page; - buffered_iopage_t *buffered_io_page; - xenforeignmemory_resource_handle *fres; - QEMUTimer *buffered_io_timer; - CPUState **cpu_by_vcpu_id; - /* the evtchn port for polling the notification, */ - evtchn_port_t *ioreq_local_port; - /* evtchn remote and local ports for buffered io */ - evtchn_port_t bufioreq_remote_port; - evtchn_port_t bufioreq_local_port; - /* the evtchn fd for polling */ - xenevtchn_handle *xce_handle; - /* which vcpu we are serving */ - int send_vcpu; - - struct xs_handle *xenstore; - MemoryListener memory_listener; - MemoryListener io_listener; - QLIST_HEAD(, XenPciDevice) dev_list; - DeviceListener device_listener; - - Notifier exit; -} XenIOState; - /* Xen specific function for piix pci */ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) @@ -247,42 +174,6 @@ static void xen_ram_init(PCMachineState *pcms, } } -void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, - Error **errp) -{ - unsigned long nr_pfn; - xen_pfn_t *pfn_list; - int i; - - if (runstate_check(RUN_STATE_INMIGRATE)) { - /* RAM already populated in Xen */ - fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT - " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n", - __func__, size, ram_addr); - return; - } - - if (mr == &ram_memory) { - return; - } - - trace_xen_ram_alloc(ram_addr, size); - - nr_pfn = size >> TARGET_PAGE_BITS; - pfn_list = g_malloc(sizeof (*pfn_list) * nr_pfn); - - for (i = 0; i < nr_pfn; i++) { - pfn_list[i] = (ram_addr >> TARGET_PAGE_BITS) + i; - } - - if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { - error_setg(errp, "xen: failed to populate ram at " RAM_ADDR_FMT, - ram_addr); - } - - g_free(pfn_list); -} - static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size) { XenPhysmap *physmap = NULL; @@ -472,144 +363,6 @@ static int xen_remove_from_physmap(XenIOState *state, return 0; } -static void xen_set_memory(struct MemoryListener *listener, - MemoryRegionSection *section, - bool add) -{ - XenIOState *state = container_of(listener, XenIOState, memory_listener); - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); - hvmmem_type_t mem_type; - - if (section->mr == &ram_memory) { - return; - } else { - if (add) { - xen_map_memory_section(xen_domid, state->ioservid, - section); - } else { - xen_unmap_memory_section(xen_domid, state->ioservid, - section); - } - } - - if (!memory_region_is_ram(section->mr)) { - return; - } - - if (log_dirty != add) { - return; - } - - trace_xen_client_set_memory(start_addr, size, log_dirty); - - start_addr &= TARGET_PAGE_MASK; - size = TARGET_PAGE_ALIGN(size); - - if (add) { - if (!memory_region_is_rom(section->mr)) { - xen_add_to_physmap(state, start_addr, size, - section->mr, section->offset_within_region); - } else { - mem_type = HVMMEM_ram_ro; - if (xen_set_mem_type(xen_domid, mem_type, - start_addr >> TARGET_PAGE_BITS, - size >> TARGET_PAGE_BITS)) { - DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", - start_addr); - } - } - } else { - if (xen_remove_from_physmap(state, start_addr, size) < 0) { - DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); - } - } -} - -static void xen_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - memory_region_ref(section->mr); - xen_set_memory(listener, section, true); -} - -static void xen_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - xen_set_memory(listener, section, false); - memory_region_unref(section->mr); -} - -static void xen_io_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - XenIOState *state = container_of(listener, XenIOState, io_listener); - MemoryRegion *mr = section->mr; - - if (mr->ops == &unassigned_io_ops) { - return; - } - - memory_region_ref(mr); - - xen_map_io_section(xen_domid, state->ioservid, section); -} - -static void xen_io_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - XenIOState *state = container_of(listener, XenIOState, io_listener); - MemoryRegion *mr = section->mr; - - if (mr->ops == &unassigned_io_ops) { - return; - } - - xen_unmap_io_section(xen_domid, state->ioservid, section); - - memory_region_unref(mr); -} - -static void xen_device_realize(DeviceListener *listener, - DeviceState *dev) -{ - XenIOState *state = container_of(listener, XenIOState, device_listener); - - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - XenPciDevice *xendev = g_new(XenPciDevice, 1); - - xendev->pci_dev = pci_dev; - xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), - pci_dev->devfn); - QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); - - xen_map_pcidev(xen_domid, state->ioservid, pci_dev); - } -} - -static void xen_device_unrealize(DeviceListener *listener, - DeviceState *dev) -{ - XenIOState *state = container_of(listener, XenIOState, device_listener); - - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - XenPciDevice *xendev, *next; - - xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); - - QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { - if (xendev->pci_dev == pci_dev) { - QLIST_REMOVE(xendev, entry); - g_free(xendev); - break; - } - } - } -} - static void xen_sync_dirty_bitmap(XenIOState *state, hwaddr start_addr, ram_addr_t size) @@ -717,277 +470,6 @@ static MemoryListener xen_memory_listener = { .priority = 10, }; -static MemoryListener xen_io_listener = { - .name = "xen-io", - .region_add = xen_io_add, - .region_del = xen_io_del, - .priority = 10, -}; - -static DeviceListener xen_device_listener = { - .realize = xen_device_realize, - .unrealize = xen_device_unrealize, -}; - -/* get the ioreq packets from share mem */ -static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) -{ - ioreq_t *req = xen_vcpu_ioreq(state->shared_page, vcpu); - - if (req->state != STATE_IOREQ_READY) { - DPRINTF("I/O request not ready: " - "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %u, size: %u\n", - req->state, req->data_is_ptr, req->addr, - req->data, req->count, req->size); - return NULL; - } - - xen_rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ - - req->state = STATE_IOREQ_INPROCESS; - return req; -} - -/* use poll to get the port notification */ -/* ioreq_vec--out,the */ -/* retval--the number of ioreq packet */ -static ioreq_t *cpu_get_ioreq(XenIOState *state) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; - int i; - evtchn_port_t port; - - port = qemu_xen_evtchn_pending(state->xce_handle); - if (port == state->bufioreq_local_port) { - timer_mod(state->buffered_io_timer, - BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - return NULL; - } - - if (port != -1) { - for (i = 0; i < max_cpus; i++) { - if (state->ioreq_local_port[i] == port) { - break; - } - } - - if (i == max_cpus) { - hw_error("Fatal error while trying to get io event!\n"); - } - - /* unmask the wanted port again */ - qemu_xen_evtchn_unmask(state->xce_handle, port); - - /* get the io packet from shared memory */ - state->send_vcpu = i; - return cpu_get_ioreq_from_shared_memory(state, i); - } - - /* read error or read nothing */ - return NULL; -} - -static uint32_t do_inp(uint32_t addr, unsigned long size) -{ - switch (size) { - case 1: - return cpu_inb(addr); - case 2: - return cpu_inw(addr); - case 4: - return cpu_inl(addr); - default: - hw_error("inp: bad size: %04x %lx", addr, size); - } -} - -static void do_outp(uint32_t addr, - unsigned long size, uint32_t val) -{ - switch (size) { - case 1: - return cpu_outb(addr, val); - case 2: - return cpu_outw(addr, val); - case 4: - return cpu_outl(addr, val); - default: - hw_error("outp: bad size: %04x %lx", addr, size); - } -} - -/* - * Helper functions which read/write an object from/to physical guest - * memory, as part of the implementation of an ioreq. - * - * Equivalent to - * cpu_physical_memory_rw(addr + (req->df ? -1 : +1) * req->size * i, - * val, req->size, 0/1) - * except without the integer overflow problems. - */ -static void rw_phys_req_item(hwaddr addr, - ioreq_t *req, uint32_t i, void *val, int rw) -{ - /* Do everything unsigned so overflow just results in a truncated result - * and accesses to undesired parts of guest memory, which is up - * to the guest */ - hwaddr offset = (hwaddr)req->size * i; - if (req->df) { - addr -= offset; - } else { - addr += offset; - } - cpu_physical_memory_rw(addr, val, req->size, rw); -} - -static inline void read_phys_req_item(hwaddr addr, - ioreq_t *req, uint32_t i, void *val) -{ - rw_phys_req_item(addr, req, i, val, 0); -} -static inline void write_phys_req_item(hwaddr addr, - ioreq_t *req, uint32_t i, void *val) -{ - rw_phys_req_item(addr, req, i, val, 1); -} - - -static void cpu_ioreq_pio(ioreq_t *req) -{ - uint32_t i; - - trace_cpu_ioreq_pio(req, req->dir, req->df, req->data_is_ptr, req->addr, - req->data, req->count, req->size); - - if (req->size > sizeof(uint32_t)) { - hw_error("PIO: bad size (%u)", req->size); - } - - if (req->dir == IOREQ_READ) { - if (!req->data_is_ptr) { - req->data = do_inp(req->addr, req->size); - trace_cpu_ioreq_pio_read_reg(req, req->data, req->addr, - req->size); - } else { - uint32_t tmp; - - for (i = 0; i < req->count; i++) { - tmp = do_inp(req->addr, req->size); - write_phys_req_item(req->data, req, i, &tmp); - } - } - } else if (req->dir == IOREQ_WRITE) { - if (!req->data_is_ptr) { - trace_cpu_ioreq_pio_write_reg(req, req->data, req->addr, - req->size); - do_outp(req->addr, req->size, req->data); - } else { - for (i = 0; i < req->count; i++) { - uint32_t tmp = 0; - - read_phys_req_item(req->data, req, i, &tmp); - do_outp(req->addr, req->size, tmp); - } - } - } -} - -static void cpu_ioreq_move(ioreq_t *req) -{ - uint32_t i; - - trace_cpu_ioreq_move(req, req->dir, req->df, req->data_is_ptr, req->addr, - req->data, req->count, req->size); - - if (req->size > sizeof(req->data)) { - hw_error("MMIO: bad size (%u)", req->size); - } - - if (!req->data_is_ptr) { - if (req->dir == IOREQ_READ) { - for (i = 0; i < req->count; i++) { - read_phys_req_item(req->addr, req, i, &req->data); - } - } else if (req->dir == IOREQ_WRITE) { - for (i = 0; i < req->count; i++) { - write_phys_req_item(req->addr, req, i, &req->data); - } - } - } else { - uint64_t tmp; - - if (req->dir == IOREQ_READ) { - for (i = 0; i < req->count; i++) { - read_phys_req_item(req->addr, req, i, &tmp); - write_phys_req_item(req->data, req, i, &tmp); - } - } else if (req->dir == IOREQ_WRITE) { - for (i = 0; i < req->count; i++) { - read_phys_req_item(req->data, req, i, &tmp); - write_phys_req_item(req->addr, req, i, &tmp); - } - } - } -} - -static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) -{ - uint32_t sbdf = req->addr >> 32; - uint32_t reg = req->addr; - XenPciDevice *xendev; - - if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && - req->size != sizeof(uint32_t)) { - hw_error("PCI config access: bad size (%u)", req->size); - } - - if (req->count != 1) { - hw_error("PCI config access: bad count (%u)", req->count); - } - - QLIST_FOREACH(xendev, &state->dev_list, entry) { - if (xendev->sbdf != sbdf) { - continue; - } - - if (!req->data_is_ptr) { - if (req->dir == IOREQ_READ) { - req->data = pci_host_config_read_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - req->size); - trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, - req->size, req->data); - } else if (req->dir == IOREQ_WRITE) { - trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, - req->size, req->data); - pci_host_config_write_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - req->data, req->size); - } - } else { - uint32_t tmp; - - if (req->dir == IOREQ_READ) { - tmp = pci_host_config_read_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - req->size); - trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, - req->size, tmp); - write_phys_req_item(req->data, req, 0, &tmp); - } else if (req->dir == IOREQ_WRITE) { - read_phys_req_item(req->data, req, 0, &tmp); - trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, - req->size, tmp); - pci_host_config_write_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - tmp, req->size); - } - } - } -} - static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) { X86CPU *cpu; @@ -1031,226 +513,6 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) current_cpu = NULL; } -static void handle_ioreq(XenIOState *state, ioreq_t *req) -{ - trace_handle_ioreq(req, req->type, req->dir, req->df, req->data_is_ptr, - req->addr, req->data, req->count, req->size); - - if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && - (req->size < sizeof (target_ulong))) { - req->data &= ((target_ulong) 1 << (8 * req->size)) - 1; - } - - if (req->dir == IOREQ_WRITE) - trace_handle_ioreq_write(req, req->type, req->df, req->data_is_ptr, - req->addr, req->data, req->count, req->size); - - switch (req->type) { - case IOREQ_TYPE_PIO: - cpu_ioreq_pio(req); - break; - case IOREQ_TYPE_COPY: - cpu_ioreq_move(req); - break; - case IOREQ_TYPE_VMWARE_PORT: - handle_vmport_ioreq(state, req); - break; - case IOREQ_TYPE_TIMEOFFSET: - break; - case IOREQ_TYPE_INVALIDATE: - xen_invalidate_map_cache(); - break; - case IOREQ_TYPE_PCI_CONFIG: - cpu_ioreq_config(state, req); - break; - default: - hw_error("Invalid ioreq type 0x%x\n", req->type); - } - if (req->dir == IOREQ_READ) { - trace_handle_ioreq_read(req, req->type, req->df, req->data_is_ptr, - req->addr, req->data, req->count, req->size); - } -} - -static bool handle_buffered_iopage(XenIOState *state) -{ - buffered_iopage_t *buf_page = state->buffered_io_page; - buf_ioreq_t *buf_req = NULL; - bool handled_ioreq = false; - ioreq_t req; - int qw; - - if (!buf_page) { - return 0; - } - - memset(&req, 0x00, sizeof(req)); - req.state = STATE_IOREQ_READY; - req.count = 1; - req.dir = IOREQ_WRITE; - - for (;;) { - uint32_t rdptr = buf_page->read_pointer, wrptr; - - xen_rmb(); - wrptr = buf_page->write_pointer; - xen_rmb(); - if (rdptr != buf_page->read_pointer) { - continue; - } - if (rdptr == wrptr) { - break; - } - buf_req = &buf_page->buf_ioreq[rdptr % IOREQ_BUFFER_SLOT_NUM]; - req.size = 1U << buf_req->size; - req.addr = buf_req->addr; - req.data = buf_req->data; - req.type = buf_req->type; - xen_rmb(); - qw = (req.size == 8); - if (qw) { - if (rdptr + 1 == wrptr) { - hw_error("Incomplete quad word buffered ioreq"); - } - buf_req = &buf_page->buf_ioreq[(rdptr + 1) % - IOREQ_BUFFER_SLOT_NUM]; - req.data |= ((uint64_t)buf_req->data) << 32; - xen_rmb(); - } - - handle_ioreq(state, &req); - - /* Only req.data may get updated by handle_ioreq(), albeit even that - * should not happen as such data would never make it to the guest (we - * can only usefully see writes here after all). - */ - assert(req.state == STATE_IOREQ_READY); - assert(req.count == 1); - assert(req.dir == IOREQ_WRITE); - assert(!req.data_is_ptr); - - qatomic_add(&buf_page->read_pointer, qw + 1); - handled_ioreq = true; - } - - return handled_ioreq; -} - -static void handle_buffered_io(void *opaque) -{ - XenIOState *state = opaque; - - if (handle_buffered_iopage(state)) { - timer_mod(state->buffered_io_timer, - BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - } else { - timer_del(state->buffered_io_timer); - qemu_xen_evtchn_unmask(state->xce_handle, state->bufioreq_local_port); - } -} - -static void cpu_handle_ioreq(void *opaque) -{ - XenIOState *state = opaque; - ioreq_t *req = cpu_get_ioreq(state); - - handle_buffered_iopage(state); - if (req) { - ioreq_t copy = *req; - - xen_rmb(); - handle_ioreq(state, ©); - req->data = copy.data; - - if (req->state != STATE_IOREQ_INPROCESS) { - fprintf(stderr, "Badness in I/O request ... not in service?!: " - "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %u, size: %u, type: %u\n", - req->state, req->data_is_ptr, req->addr, - req->data, req->count, req->size, req->type); - destroy_hvm_domain(false); - return; - } - - xen_wmb(); /* Update ioreq contents /then/ update state. */ - - /* - * We do this before we send the response so that the tools - * have the opportunity to pick up on the reset before the - * guest resumes and does a hlt with interrupts disabled which - * causes Xen to powerdown the domain. - */ - if (runstate_is_running()) { - ShutdownCause request; - - if (qemu_shutdown_requested_get()) { - destroy_hvm_domain(false); - } - request = qemu_reset_requested_get(); - if (request) { - qemu_system_reset(request); - destroy_hvm_domain(true); - } - } - - req->state = STATE_IORESP_READY; - qemu_xen_evtchn_notify(state->xce_handle, - state->ioreq_local_port[state->send_vcpu]); - } -} - -static void xen_main_loop_prepare(XenIOState *state) -{ - int evtchn_fd = -1; - - if (state->xce_handle != NULL) { - evtchn_fd = qemu_xen_evtchn_fd(state->xce_handle); - } - - state->buffered_io_timer = timer_new_ms(QEMU_CLOCK_REALTIME, handle_buffered_io, - state); - - if (evtchn_fd != -1) { - CPUState *cpu_state; - - DPRINTF("%s: Init cpu_by_vcpu_id\n", __func__); - CPU_FOREACH(cpu_state) { - DPRINTF("%s: cpu_by_vcpu_id[%d]=%p\n", - __func__, cpu_state->cpu_index, cpu_state); - state->cpu_by_vcpu_id[cpu_state->cpu_index] = cpu_state; - } - qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state); - } -} - - -static void xen_hvm_change_state_handler(void *opaque, bool running, - RunState rstate) -{ - XenIOState *state = opaque; - - if (running) { - xen_main_loop_prepare(state); - } - - xen_set_ioreq_server_state(xen_domid, - state->ioservid, - (rstate == RUN_STATE_RUNNING)); -} - -static void xen_exit_notifier(Notifier *n, void *data) -{ - XenIOState *state = container_of(n, XenIOState, exit); - - xen_destroy_ioreq_server(xen_domid, state->ioservid); - if (state->fres != NULL) { - xenforeignmemory_unmap_resource(xen_fmem, state->fres); - } - - qemu_xen_evtchn_close(state->xce_handle); - xs_daemon_close(state->xenstore); -} - #ifdef XEN_COMPAT_PHYSMAP static void xen_read_physmap(XenIOState *state) { @@ -1310,175 +572,17 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } -static int xen_map_ioreq_server(XenIOState *state) -{ - void *addr = NULL; - xen_pfn_t ioreq_pfn; - xen_pfn_t bufioreq_pfn; - evtchn_port_t bufioreq_evtchn; - int rc; - - /* - * Attempt to map using the resource API and fall back to normal - * foreign mapping if this is not supported. - */ - QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); - QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); - state->fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, - XENMEM_resource_ioreq_server, - state->ioservid, 0, 2, - &addr, - PROT_READ | PROT_WRITE, 0); - if (state->fres != NULL) { - trace_xen_map_resource_ioreq(state->ioservid, addr); - state->buffered_io_page = addr; - state->shared_page = addr + TARGET_PAGE_SIZE; - } else if (errno != EOPNOTSUPP) { - error_report("failed to map ioreq server resources: error %d handle=%p", - errno, xen_xc); - return -1; - } - - rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - (state->shared_page == NULL) ? - &ioreq_pfn : NULL, - (state->buffered_io_page == NULL) ? - &bufioreq_pfn : NULL, - &bufioreq_evtchn); - if (rc < 0) { - error_report("failed to get ioreq server info: error %d handle=%p", - errno, xen_xc); - return rc; - } - - if (state->shared_page == NULL) { - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); - } - } - - if (state->buffered_io_page == NULL) { - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &bufioreq_pfn, - NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - return -1; - } - } - - if (state->shared_page == NULL || state->buffered_io_page == NULL) { - return -1; - } - - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->bufioreq_remote_port = bufioreq_evtchn; - - return 0; -} - void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) { MachineState *ms = MACHINE(pcms); unsigned int max_cpus = ms->smp.max_cpus; - int i, rc; + int rc; xen_pfn_t ioreq_pfn; XenIOState *state; - setup_xen_backend_ops(); - state = g_new0(XenIOState, 1); - state->xce_handle = qemu_xen_evtchn_open(); - if (state->xce_handle == NULL) { - perror("xen: event channel open"); - goto err; - } - - state->xenstore = xs_daemon_open(); - if (state->xenstore == NULL) { - perror("xen: xenstore open"); - goto err; - } - - xen_create_ioreq_server(xen_domid, &state->ioservid); - - state->exit.notify = xen_exit_notifier; - qemu_add_exit_notifier(&state->exit); - - /* - * Register wake-up support in QMP query-current-machine API - */ - qemu_register_wakeup_support(); - - rc = xen_map_ioreq_server(state); - if (rc < 0) { - goto err; - } - - /* Note: cpus is empty at this point in init */ - state->cpu_by_vcpu_id = g_new0(CPUState *, max_cpus); - - rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); - if (rc < 0) { - error_report("failed to enable ioreq server info: error %d handle=%p", - errno, xen_xc); - goto err; - } - - state->ioreq_local_port = g_new0(evtchn_port_t, max_cpus); - - /* FIXME: how about if we overflow the page here? */ - for (i = 0; i < max_cpus; i++) { - rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, - xen_vcpu_eport(state->shared_page, - i)); - if (rc == -1) { - error_report("shared evtchn %d bind error %d", i, errno); - goto err; - } - state->ioreq_local_port[i] = rc; - } - - rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, - state->bufioreq_remote_port); - if (rc == -1) { - error_report("buffered evtchn bind error %d", errno); - goto err; - } - state->bufioreq_local_port = rc; - - /* Init RAM management */ -#ifdef XEN_COMPAT_PHYSMAP - xen_map_cache_init(xen_phys_offset_to_gaddr, state); -#else - xen_map_cache_init(NULL, state); -#endif - - qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); - - state->memory_listener = xen_memory_listener; - memory_listener_register(&state->memory_listener, &address_space_memory); - - state->io_listener = xen_io_listener; - memory_listener_register(&state->io_listener, &address_space_io); - - state->device_listener = xen_device_listener; - QLIST_INIT(&state->dev_list); - device_listener_register(&state->device_listener); - - xen_bus_init(); - xen_be_init(); + xen_register_ioreq(state, max_cpus, xen_memory_listener); QLIST_INIT(&xen_physmap); xen_read_physmap(state); @@ -1518,59 +622,11 @@ err: exit(1); } -void destroy_hvm_domain(bool reboot) -{ - xc_interface *xc_handle; - int sts; - int rc; - - unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; - - if (xen_dmod) { - rc = xendevicemodel_shutdown(xen_dmod, xen_domid, reason); - if (!rc) { - return; - } - if (errno != ENOTTY /* old Xen */) { - perror("xendevicemodel_shutdown failed"); - } - /* well, try the old thing then */ - } - - xc_handle = xc_interface_open(0, 0, 0); - if (xc_handle == NULL) { - fprintf(stderr, "Cannot acquire xenctrl handle\n"); - } else { - sts = xc_domain_shutdown(xc_handle, xen_domid, reason); - if (sts != 0) { - fprintf(stderr, "xc_domain_shutdown failed to issue %s, " - "sts %d, %s\n", reboot ? "reboot" : "poweroff", - sts, strerror(errno)); - } else { - fprintf(stderr, "Issued domain %d %s\n", xen_domid, - reboot ? "reboot" : "poweroff"); - } - xc_interface_close(xc_handle); - } -} - void xen_register_framebuffer(MemoryRegion *mr) { framebuffer = mr; } -void xen_shutdown_fatal_error(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "Will destroy the domain.\n"); - /* destroy the domain */ - qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); -} - void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) { if (unlikely(xen_in_migration)) { @@ -1602,3 +658,57 @@ void qmp_xen_set_global_dirty_log(bool enable, Error **errp) memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); } } + +void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, + bool add) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); + hvmmem_type_t mem_type; + + if (!memory_region_is_ram(section->mr)) { + return; + } + + if (log_dirty != add) { + return; + } + + trace_xen_client_set_memory(start_addr, size, log_dirty); + + start_addr &= TARGET_PAGE_MASK; + size = TARGET_PAGE_ALIGN(size); + + if (add) { + if (!memory_region_is_rom(section->mr)) { + xen_add_to_physmap(state, start_addr, size, + section->mr, section->offset_within_region); + } else { + mem_type = HVMMEM_ram_ro; + if (xen_set_mem_type(xen_domid, mem_type, + start_addr >> TARGET_PAGE_BITS, + size >> TARGET_PAGE_BITS)) { + DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", + start_addr); + } + } + } else { + if (xen_remove_from_physmap(state, start_addr, size) < 0) { + DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); + } + } +} + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req) +{ + switch (req->type) { + case IOREQ_TYPE_VMWARE_PORT: + handle_vmport_ioreq(state, req); + break; + default: + hw_error("Invalid ioreq type 0x%x\n", req->type); + } + + return; +} diff --git a/hw/xen/meson.build b/hw/xen/meson.build index 202752e557..afd20754a1 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -29,4 +29,7 @@ specific_ss.add_all(when: ['CONFIG_XEN', xen], if_true: xen_specific_ss) xen_ss = ss.source_set() -xen_ss.add(when: 'CONFIG_XEN', if_true: files('xen-mapcache.c')) +xen_ss.add(when: 'CONFIG_XEN', if_true: files( + 'xen-mapcache.c', + 'xen-hvm-common.c', +)) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index f977c7c8c6..67a6c41926 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -42,6 +42,20 @@ xs_node_vscanf(char *path, char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" +# xen-hvm.c +xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: 0x%lx, size 0x%lx" +xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "0x%"PRIx64" size 0x%lx, log_dirty %i" +handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" +cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" +cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" +cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" +cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" + # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 xen_remap_bucket(uint64_t index) "index 0x%"PRIx64 diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c new file mode 100644 index 0000000000..a31b067404 --- /dev/null +++ b/hw/xen/xen-hvm-common.c @@ -0,0 +1,860 @@ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "trace.h" + +#include "hw/pci/pci_host.h" +#include "hw/xen/xen-hvm-common.h" +#include "hw/xen/xen-bus.h" +#include "hw/boards.h" +#include "hw/xen/arch_hvm.h" + +MemoryRegion ram_memory; + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, + Error **errp) +{ + unsigned long nr_pfn; + xen_pfn_t *pfn_list; + int i; + + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* RAM already populated in Xen */ + fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT + " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n", + __func__, size, ram_addr); + return; + } + + if (mr == &ram_memory) { + return; + } + + trace_xen_ram_alloc(ram_addr, size); + + nr_pfn = size >> TARGET_PAGE_BITS; + pfn_list = g_malloc(sizeof (*pfn_list) * nr_pfn); + + for (i = 0; i < nr_pfn; i++) { + pfn_list[i] = (ram_addr >> TARGET_PAGE_BITS) + i; + } + + if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { + error_setg(errp, "xen: failed to populate ram at " RAM_ADDR_FMT, + ram_addr); + } + + g_free(pfn_list); +} + +static void xen_set_memory(struct MemoryListener *listener, + MemoryRegionSection *section, + bool add) +{ + XenIOState *state = container_of(listener, XenIOState, memory_listener); + + if (section->mr == &ram_memory) { + return; + } else { + if (add) { + xen_map_memory_section(xen_domid, state->ioservid, + section); + } else { + xen_unmap_memory_section(xen_domid, state->ioservid, + section); + } + } + + arch_xen_set_memory(state, section, add); +} + +void xen_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + memory_region_ref(section->mr); + xen_set_memory(listener, section, true); +} + +void xen_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + xen_set_memory(listener, section, false); + memory_region_unref(section->mr); +} + +void xen_io_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, io_listener); + MemoryRegion *mr = section->mr; + + if (mr->ops == &unassigned_io_ops) { + return; + } + + memory_region_ref(mr); + + xen_map_io_section(xen_domid, state->ioservid, section); +} + +void xen_io_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, io_listener); + MemoryRegion *mr = section->mr; + + if (mr->ops == &unassigned_io_ops) { + return; + } + + xen_unmap_io_section(xen_domid, state->ioservid, section); + + memory_region_unref(mr); +} + +void xen_device_realize(DeviceListener *listener, + DeviceState *dev) +{ + XenIOState *state = container_of(listener, XenIOState, device_listener); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev = g_new(XenPciDevice, 1); + + xendev->pci_dev = pci_dev; + xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), + pci_dev->devfn); + QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); + + xen_map_pcidev(xen_domid, state->ioservid, pci_dev); + } +} + +void xen_device_unrealize(DeviceListener *listener, + DeviceState *dev) +{ + XenIOState *state = container_of(listener, XenIOState, device_listener); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev, *next; + + xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); + + QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { + if (xendev->pci_dev == pci_dev) { + QLIST_REMOVE(xendev, entry); + g_free(xendev); + break; + } + } + } +} + +MemoryListener xen_io_listener = { + .name = "xen-io", + .region_add = xen_io_add, + .region_del = xen_io_del, + .priority = 10, +}; + +DeviceListener xen_device_listener = { + .realize = xen_device_realize, + .unrealize = xen_device_unrealize, +}; + +/* get the ioreq packets from share mem */ +static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) +{ + ioreq_t *req = xen_vcpu_ioreq(state->shared_page, vcpu); + + if (req->state != STATE_IOREQ_READY) { + DPRINTF("I/O request not ready: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %u, size: %u\n", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + return NULL; + } + + xen_rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ + + req->state = STATE_IOREQ_INPROCESS; + return req; +} + +/* use poll to get the port notification */ +/* ioreq_vec--out,the */ +/* retval--the number of ioreq packet */ +static ioreq_t *cpu_get_ioreq(XenIOState *state) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int max_cpus = ms->smp.max_cpus; + int i; + evtchn_port_t port; + + port = qemu_xen_evtchn_pending(state->xce_handle); + if (port == state->bufioreq_local_port) { + timer_mod(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + return NULL; + } + + if (port != -1) { + for (i = 0; i < max_cpus; i++) { + if (state->ioreq_local_port[i] == port) { + break; + } + } + + if (i == max_cpus) { + hw_error("Fatal error while trying to get io event!\n"); + } + + /* unmask the wanted port again */ + qemu_xen_evtchn_unmask(state->xce_handle, port); + + /* get the io packet from shared memory */ + state->send_vcpu = i; + return cpu_get_ioreq_from_shared_memory(state, i); + } + + /* read error or read nothing */ + return NULL; +} + +static uint32_t do_inp(uint32_t addr, unsigned long size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + default: + hw_error("inp: bad size: %04x %lx", addr, size); + } +} + +static void do_outp(uint32_t addr, + unsigned long size, uint32_t val) +{ + switch (size) { + case 1: + return cpu_outb(addr, val); + case 2: + return cpu_outw(addr, val); + case 4: + return cpu_outl(addr, val); + default: + hw_error("outp: bad size: %04x %lx", addr, size); + } +} + +/* + * Helper functions which read/write an object from/to physical guest + * memory, as part of the implementation of an ioreq. + * + * Equivalent to + * cpu_physical_memory_rw(addr + (req->df ? -1 : +1) * req->size * i, + * val, req->size, 0/1) + * except without the integer overflow problems. + */ +static void rw_phys_req_item(hwaddr addr, + ioreq_t *req, uint32_t i, void *val, int rw) +{ + /* Do everything unsigned so overflow just results in a truncated result + * and accesses to undesired parts of guest memory, which is up + * to the guest */ + hwaddr offset = (hwaddr)req->size * i; + if (req->df) { + addr -= offset; + } else { + addr += offset; + } + cpu_physical_memory_rw(addr, val, req->size, rw); +} + +static inline void read_phys_req_item(hwaddr addr, + ioreq_t *req, uint32_t i, void *val) +{ + rw_phys_req_item(addr, req, i, val, 0); +} +static inline void write_phys_req_item(hwaddr addr, + ioreq_t *req, uint32_t i, void *val) +{ + rw_phys_req_item(addr, req, i, val, 1); +} + + +void cpu_ioreq_pio(ioreq_t *req) +{ + uint32_t i; + + trace_cpu_ioreq_pio(req, req->dir, req->df, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + + if (req->size > sizeof(uint32_t)) { + hw_error("PIO: bad size (%u)", req->size); + } + + if (req->dir == IOREQ_READ) { + if (!req->data_is_ptr) { + req->data = do_inp(req->addr, req->size); + trace_cpu_ioreq_pio_read_reg(req, req->data, req->addr, + req->size); + } else { + uint32_t tmp; + + for (i = 0; i < req->count; i++) { + tmp = do_inp(req->addr, req->size); + write_phys_req_item(req->data, req, i, &tmp); + } + } + } else if (req->dir == IOREQ_WRITE) { + if (!req->data_is_ptr) { + trace_cpu_ioreq_pio_write_reg(req, req->data, req->addr, + req->size); + do_outp(req->addr, req->size, req->data); + } else { + for (i = 0; i < req->count; i++) { + uint32_t tmp = 0; + + read_phys_req_item(req->data, req, i, &tmp); + do_outp(req->addr, req->size, tmp); + } + } + } +} + +static void cpu_ioreq_move(ioreq_t *req) +{ + uint32_t i; + + trace_cpu_ioreq_move(req, req->dir, req->df, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + + if (req->size > sizeof(req->data)) { + hw_error("MMIO: bad size (%u)", req->size); + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_phys_req_item(req->addr, req, i, &req->data); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + write_phys_req_item(req->addr, req, i, &req->data); + } + } + } else { + uint64_t tmp; + + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_phys_req_item(req->addr, req, i, &tmp); + write_phys_req_item(req->data, req, i, &tmp); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + read_phys_req_item(req->data, req, i, &tmp); + write_phys_req_item(req->addr, req, i, &tmp); + } + } + } +} + +static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) +{ + uint32_t sbdf = req->addr >> 32; + uint32_t reg = req->addr; + XenPciDevice *xendev; + + if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) { + hw_error("PCI config access: bad size (%u)", req->size); + } + + if (req->count != 1) { + hw_error("PCI config access: bad count (%u)", req->count); + } + + QLIST_FOREACH(xendev, &state->dev_list, entry) { + if (xendev->sbdf != sbdf) { + continue; + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + req->data = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, req->data); + } else if (req->dir == IOREQ_WRITE) { + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, req->data); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->data, req->size); + } + } else { + uint32_t tmp; + + if (req->dir == IOREQ_READ) { + tmp = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, tmp); + write_phys_req_item(req->data, req, 0, &tmp); + } else if (req->dir == IOREQ_WRITE) { + read_phys_req_item(req->data, req, 0, &tmp); + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, tmp); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + tmp, req->size); + } + } + } +} + +static void handle_ioreq(XenIOState *state, ioreq_t *req) +{ + trace_handle_ioreq(req, req->type, req->dir, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + + if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && + (req->size < sizeof (target_ulong))) { + req->data &= ((target_ulong) 1 << (8 * req->size)) - 1; + } + + if (req->dir == IOREQ_WRITE) + trace_handle_ioreq_write(req, req->type, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + + switch (req->type) { + case IOREQ_TYPE_PIO: + cpu_ioreq_pio(req); + break; + case IOREQ_TYPE_COPY: + cpu_ioreq_move(req); + break; + case IOREQ_TYPE_TIMEOFFSET: + break; + case IOREQ_TYPE_INVALIDATE: + xen_invalidate_map_cache(); + break; + case IOREQ_TYPE_PCI_CONFIG: + cpu_ioreq_config(state, req); + break; + default: + arch_handle_ioreq(state, req); + } + if (req->dir == IOREQ_READ) { + trace_handle_ioreq_read(req, req->type, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + } +} + +static bool handle_buffered_iopage(XenIOState *state) +{ + buffered_iopage_t *buf_page = state->buffered_io_page; + buf_ioreq_t *buf_req = NULL; + bool handled_ioreq = false; + ioreq_t req; + int qw; + + if (!buf_page) { + return 0; + } + + memset(&req, 0x00, sizeof(req)); + req.state = STATE_IOREQ_READY; + req.count = 1; + req.dir = IOREQ_WRITE; + + for (;;) { + uint32_t rdptr = buf_page->read_pointer, wrptr; + + xen_rmb(); + wrptr = buf_page->write_pointer; + xen_rmb(); + if (rdptr != buf_page->read_pointer) { + continue; + } + if (rdptr == wrptr) { + break; + } + buf_req = &buf_page->buf_ioreq[rdptr % IOREQ_BUFFER_SLOT_NUM]; + req.size = 1U << buf_req->size; + req.addr = buf_req->addr; + req.data = buf_req->data; + req.type = buf_req->type; + xen_rmb(); + qw = (req.size == 8); + if (qw) { + if (rdptr + 1 == wrptr) { + hw_error("Incomplete quad word buffered ioreq"); + } + buf_req = &buf_page->buf_ioreq[(rdptr + 1) % + IOREQ_BUFFER_SLOT_NUM]; + req.data |= ((uint64_t)buf_req->data) << 32; + xen_rmb(); + } + + handle_ioreq(state, &req); + + /* Only req.data may get updated by handle_ioreq(), albeit even that + * should not happen as such data would never make it to the guest (we + * can only usefully see writes here after all). + */ + assert(req.state == STATE_IOREQ_READY); + assert(req.count == 1); + assert(req.dir == IOREQ_WRITE); + assert(!req.data_is_ptr); + + qatomic_add(&buf_page->read_pointer, qw + 1); + handled_ioreq = true; + } + + return handled_ioreq; +} + +static void handle_buffered_io(void *opaque) +{ + XenIOState *state = opaque; + + if (handle_buffered_iopage(state)) { + timer_mod(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } else { + timer_del(state->buffered_io_timer); + qemu_xen_evtchn_unmask(state->xce_handle, state->bufioreq_local_port); + } +} + +static void cpu_handle_ioreq(void *opaque) +{ + XenIOState *state = opaque; + ioreq_t *req = cpu_get_ioreq(state); + + handle_buffered_iopage(state); + if (req) { + ioreq_t copy = *req; + + xen_rmb(); + handle_ioreq(state, ©); + req->data = copy.data; + + if (req->state != STATE_IOREQ_INPROCESS) { + fprintf(stderr, "Badness in I/O request ... not in service?!: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %u, size: %u, type: %u\n", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size, req->type); + destroy_hvm_domain(false); + return; + } + + xen_wmb(); /* Update ioreq contents /then/ update state. */ + + /* + * We do this before we send the response so that the tools + * have the opportunity to pick up on the reset before the + * guest resumes and does a hlt with interrupts disabled which + * causes Xen to powerdown the domain. + */ + if (runstate_is_running()) { + ShutdownCause request; + + if (qemu_shutdown_requested_get()) { + destroy_hvm_domain(false); + } + request = qemu_reset_requested_get(); + if (request) { + qemu_system_reset(request); + destroy_hvm_domain(true); + } + } + + req->state = STATE_IORESP_READY; + qemu_xen_evtchn_notify(state->xce_handle, + state->ioreq_local_port[state->send_vcpu]); + } +} + +static void xen_main_loop_prepare(XenIOState *state) +{ + int evtchn_fd = -1; + + if (state->xce_handle != NULL) { + evtchn_fd = qemu_xen_evtchn_fd(state->xce_handle); + } + + state->buffered_io_timer = timer_new_ms(QEMU_CLOCK_REALTIME, handle_buffered_io, + state); + + if (evtchn_fd != -1) { + CPUState *cpu_state; + + DPRINTF("%s: Init cpu_by_vcpu_id\n", __func__); + CPU_FOREACH(cpu_state) { + DPRINTF("%s: cpu_by_vcpu_id[%d]=%p\n", + __func__, cpu_state->cpu_index, cpu_state); + state->cpu_by_vcpu_id[cpu_state->cpu_index] = cpu_state; + } + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state); + } +} + + +void xen_hvm_change_state_handler(void *opaque, bool running, + RunState rstate) +{ + XenIOState *state = opaque; + + if (running) { + xen_main_loop_prepare(state); + } + + xen_set_ioreq_server_state(xen_domid, + state->ioservid, + (rstate == RUN_STATE_RUNNING)); +} + +void xen_exit_notifier(Notifier *n, void *data) +{ + XenIOState *state = container_of(n, XenIOState, exit); + + xen_destroy_ioreq_server(xen_domid, state->ioservid); + if (state->fres != NULL) { + xenforeignmemory_unmap_resource(xen_fmem, state->fres); + } + + qemu_xen_evtchn_close(state->xce_handle); + xs_daemon_close(state->xenstore); +} + +static int xen_map_ioreq_server(XenIOState *state) +{ + void *addr = NULL; + xen_pfn_t ioreq_pfn; + xen_pfn_t bufioreq_pfn; + evtchn_port_t bufioreq_evtchn; + int rc; + + /* + * Attempt to map using the resource API and fall back to normal + * foreign mapping if this is not supported. + */ + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); + state->fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, + XENMEM_resource_ioreq_server, + state->ioservid, 0, 2, + &addr, + PROT_READ | PROT_WRITE, 0); + if (state->fres != NULL) { + trace_xen_map_resource_ioreq(state->ioservid, addr); + state->buffered_io_page = addr; + state->shared_page = addr + XC_PAGE_SIZE; + } else if (errno != EOPNOTSUPP) { + error_report("failed to map ioreq server resources: error %d handle=%p", + errno, xen_xc); + return -1; + } + + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + (state->shared_page == NULL) ? + &ioreq_pfn : NULL, + (state->buffered_io_page == NULL) ? + &bufioreq_pfn : NULL, + &bufioreq_evtchn); + if (rc < 0) { + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + return rc; + } + + if (state->shared_page == NULL) { + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + } + } + + if (state->buffered_io_page == NULL) { + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, + NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + } + + if (state->shared_page == NULL || state->buffered_io_page == NULL) { + return -1; + } + + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + + state->bufioreq_remote_port = bufioreq_evtchn; + + return 0; +} + +void destroy_hvm_domain(bool reboot) +{ + xc_interface *xc_handle; + int sts; + int rc; + + unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; + + if (xen_dmod) { + rc = xendevicemodel_shutdown(xen_dmod, xen_domid, reason); + if (!rc) { + return; + } + if (errno != ENOTTY /* old Xen */) { + perror("xendevicemodel_shutdown failed"); + } + /* well, try the old thing then */ + } + + xc_handle = xc_interface_open(0, 0, 0); + if (xc_handle == NULL) { + fprintf(stderr, "Cannot acquire xenctrl handle\n"); + } else { + sts = xc_domain_shutdown(xc_handle, xen_domid, reason); + if (sts != 0) { + fprintf(stderr, "xc_domain_shutdown failed to issue %s, " + "sts %d, %s\n", reboot ? "reboot" : "poweroff", + sts, strerror(errno)); + } else { + fprintf(stderr, "Issued domain %d %s\n", xen_domid, + reboot ? "reboot" : "poweroff"); + } + xc_interface_close(xc_handle); + } +} + +void xen_shutdown_fatal_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Will destroy the domain.\n"); + /* destroy the domain */ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); +} + +void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, + MemoryListener xen_memory_listener) +{ + int i, rc; + + setup_xen_backend_ops(); + + state->xce_handle = qemu_xen_evtchn_open(); + if (state->xce_handle == NULL) { + perror("xen: event channel open"); + goto err; + } + + state->xenstore = xs_daemon_open(); + if (state->xenstore == NULL) { + perror("xen: xenstore open"); + goto err; + } + + xen_create_ioreq_server(xen_domid, &state->ioservid); + + state->exit.notify = xen_exit_notifier; + qemu_add_exit_notifier(&state->exit); + + /* + * Register wake-up support in QMP query-current-machine API + */ + qemu_register_wakeup_support(); + + rc = xen_map_ioreq_server(state); + if (rc < 0) { + goto err; + } + + /* Note: cpus is empty at this point in init */ + state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); + + rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); + if (rc < 0) { + error_report("failed to enable ioreq server info: error %d handle=%p", + errno, xen_xc); + goto err; + } + + state->ioreq_local_port = g_malloc0(max_cpus * sizeof (evtchn_port_t)); + + /* FIXME: how about if we overflow the page here? */ + for (i = 0; i < max_cpus; i++) { + rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, + xen_vcpu_eport(state->shared_page, + i)); + if (rc == -1) { + error_report("shared evtchn %d bind error %d", i, errno); + goto err; + } + state->ioreq_local_port[i] = rc; + } + + rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, + state->bufioreq_remote_port); + if (rc == -1) { + error_report("buffered evtchn bind error %d", errno); + goto err; + } + state->bufioreq_local_port = rc; + + /* Init RAM management */ +#ifdef XEN_COMPAT_PHYSMAP + xen_map_cache_init(xen_phys_offset_to_gaddr, state); +#else + xen_map_cache_init(NULL, state); +#endif + + qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); + + state->memory_listener = xen_memory_listener; + memory_listener_register(&state->memory_listener, &address_space_memory); + + state->io_listener = xen_io_listener; + memory_listener_register(&state->io_listener, &address_space_io); + + state->device_listener = xen_device_listener; + QLIST_INIT(&state->dev_list); + device_listener_register(&state->device_listener); + + xen_bus_init(); + + xen_be_init(); + + return; +err: + error_report("xen hardware virtual machine initialisation failed"); + exit(1); +} diff --git a/include/hw/i386/xen_arch_hvm.h b/include/hw/i386/xen_arch_hvm.h new file mode 100644 index 0000000000..1000f8f543 --- /dev/null +++ b/include/hw/i386/xen_arch_hvm.h @@ -0,0 +1,11 @@ +#ifndef HW_XEN_ARCH_I386_HVM_H +#define HW_XEN_ARCH_I386_HVM_H + +#include +#include "hw/xen/xen-hvm-common.h" + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req); +void arch_xen_set_memory(XenIOState *state, + MemoryRegionSection *section, + bool add); +#endif diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h new file mode 100644 index 0000000000..26674648d8 --- /dev/null +++ b/include/hw/xen/arch_hvm.h @@ -0,0 +1,3 @@ +#if defined(TARGET_I386) || defined(TARGET_X86_64) +#include "hw/i386/xen_arch_hvm.h" +#endif diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h new file mode 100644 index 0000000000..f9559e2885 --- /dev/null +++ b/include/hw/xen/xen-hvm-common.h @@ -0,0 +1,99 @@ +#ifndef HW_XEN_HVM_COMMON_H +#define HW_XEN_HVM_COMMON_H + +#include "qemu/osdep.h" +#include "qemu/units.h" + +#include "cpu.h" +#include "hw/pci/pci.h" +#include "hw/hw.h" +#include "hw/xen/xen_native.h" +#include "hw/xen/xen-legacy-backend.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "sysemu/xen.h" +#include "sysemu/xen-mapcache.h" +#include "qemu/error-report.h" +#include + +extern MemoryRegion ram_memory; +extern MemoryListener xen_io_listener; +extern DeviceListener xen_device_listener; + +//#define DEBUG_XEN_HVM + +#ifdef DEBUG_XEN_HVM +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) +{ + return shared_page->vcpu_ioreq[i].vp_eport; +} +static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) +{ + return &shared_page->vcpu_ioreq[vcpu]; +} + +#define BUFFER_IO_MAX_DELAY 100 + +typedef struct XenPhysmap { + hwaddr start_addr; + ram_addr_t size; + const char *name; + hwaddr phys_offset; + + QLIST_ENTRY(XenPhysmap) list; +} XenPhysmap; + +typedef struct XenPciDevice { + PCIDevice *pci_dev; + uint32_t sbdf; + QLIST_ENTRY(XenPciDevice) entry; +} XenPciDevice; + +typedef struct XenIOState { + ioservid_t ioservid; + shared_iopage_t *shared_page; + buffered_iopage_t *buffered_io_page; + xenforeignmemory_resource_handle *fres; + QEMUTimer *buffered_io_timer; + CPUState **cpu_by_vcpu_id; + /* the evtchn port for polling the notification, */ + evtchn_port_t *ioreq_local_port; + /* evtchn remote and local ports for buffered io */ + evtchn_port_t bufioreq_remote_port; + evtchn_port_t bufioreq_local_port; + /* the evtchn fd for polling */ + xenevtchn_handle *xce_handle; + /* which vcpu we are serving */ + int send_vcpu; + + struct xs_handle *xenstore; + MemoryListener memory_listener; + MemoryListener io_listener; + QLIST_HEAD(, XenPciDevice) dev_list; + DeviceListener device_listener; + + Notifier exit; +} XenIOState; + +void xen_exit_notifier(Notifier *n, void *data); + +void xen_region_add(MemoryListener *listener, MemoryRegionSection *section); +void xen_region_del(MemoryListener *listener, MemoryRegionSection *section); +void xen_io_add(MemoryListener *listener, MemoryRegionSection *section); +void xen_io_del(MemoryListener *listener, MemoryRegionSection *section); +void xen_device_realize(DeviceListener *listener, DeviceState *dev); +void xen_device_unrealize(DeviceListener *listener, DeviceState *dev); + +void xen_hvm_change_state_handler(void *opaque, bool running, RunState rstate); +void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, + MemoryListener xen_memory_listener); + +void cpu_ioreq_pio(ioreq_t *req); +#endif /* HW_XEN_HVM_COMMON_H */ From 420927c218a96c6a39cb5b1516e011506f33f68a Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 14 Jun 2023 17:03:33 -0700 Subject: [PATCH 350/412] include/hw/xen/xen_common: return error from xen_create_ioreq_server This is done to prepare for enabling xenpv support for ARM architecture. On ARM it is possible to have a functioning xenpv machine with only the PV backends and no IOREQ server. If the IOREQ server creation fails, continue to the PV backends initialization. Signed-off-by: Stefano Stabellini Signed-off-by: Vikram Garhwal Reviewed-by: Stefano Stabellini Reviewed-by: Paul Durrant --- include/hw/xen/xen_native.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/hw/xen/xen_native.h b/include/hw/xen/xen_native.h index f11eb423e3..4dce905fde 100644 --- a/include/hw/xen/xen_native.h +++ b/include/hw/xen/xen_native.h @@ -463,8 +463,8 @@ static inline void xen_unmap_pcidev(domid_t dom, PCI_FUNC(pci_dev->devfn)); } -static inline void xen_create_ioreq_server(domid_t dom, - ioservid_t *ioservid) +static inline int xen_create_ioreq_server(domid_t dom, + ioservid_t *ioservid) { int rc = xendevicemodel_create_ioreq_server(xen_dmod, dom, HVM_IOREQSRV_BUFIOREQ_ATOMIC, @@ -472,12 +472,14 @@ static inline void xen_create_ioreq_server(domid_t dom, if (rc == 0) { trace_xen_ioreq_server_create(*ioservid); - return; + return rc; } *ioservid = 0; use_default_ioreq_server = true; trace_xen_default_ioreq_server(); + + return rc; } static inline void xen_destroy_ioreq_server(domid_t dom, From 5ff5c8da948895ceb6ce42408e974488d08ba2d3 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 14 Jun 2023 17:03:34 -0700 Subject: [PATCH 351/412] hw/xen/xen-hvm-common: skip ioreq creation on ioreq registration failure On ARM it is possible to have a functioning xenpv machine with only the PV backends and no IOREQ server. If the IOREQ server creation fails continue to the PV backends initialization. Also, moved the IOREQ registration and mapping subroutine to new function xen_do_ioreq_register(). Signed-off-by: Stefano Stabellini Signed-off-by: Vikram Garhwal Reviewed-by: Stefano Stabellini Reviewed-by: Paul Durrant --- hw/xen/xen-hvm-common.c | 57 +++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index a31b067404..cb82f4b83d 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -764,27 +764,12 @@ void xen_shutdown_fatal_error(const char *fmt, ...) qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); } -void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, - MemoryListener xen_memory_listener) +static void xen_do_ioreq_register(XenIOState *state, + unsigned int max_cpus, + MemoryListener xen_memory_listener) { int i, rc; - setup_xen_backend_ops(); - - state->xce_handle = qemu_xen_evtchn_open(); - if (state->xce_handle == NULL) { - perror("xen: event channel open"); - goto err; - } - - state->xenstore = xs_daemon_open(); - if (state->xenstore == NULL) { - perror("xen: xenstore open"); - goto err; - } - - xen_create_ioreq_server(xen_domid, &state->ioservid); - state->exit.notify = xen_exit_notifier; qemu_add_exit_notifier(&state->exit); @@ -849,12 +834,46 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, QLIST_INIT(&state->dev_list); device_listener_register(&state->device_listener); + return; + +err: + error_report("xen hardware virtual machine initialisation failed"); + exit(1); +} + +void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, + MemoryListener xen_memory_listener) +{ + int rc; + + setup_xen_backend_ops(); + + state->xce_handle = qemu_xen_evtchn_open(); + if (state->xce_handle == NULL) { + perror("xen: event channel open"); + goto err; + } + + state->xenstore = xs_daemon_open(); + if (state->xenstore == NULL) { + perror("xen: xenstore open"); + goto err; + } + + rc = xen_create_ioreq_server(xen_domid, &state->ioservid); + if (!rc) { + xen_do_ioreq_register(state, max_cpus, xen_memory_listener); + } else { + warn_report("xen: failed to create ioreq server"); + } + xen_bus_init(); xen_be_init(); return; + err: - error_report("xen hardware virtual machine initialisation failed"); + error_report("xen hardware virtual machine backend registration failed"); exit(1); } From 6c4193ed1690ab140a94d8288dcd41237504e7d2 Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 14 Jun 2023 17:03:35 -0700 Subject: [PATCH 352/412] hw/xen/xen-hvm-common: Use g_new and error_report Replace g_malloc with g_new and perror with error_report. Signed-off-by: Vikram Garhwal Reviewed-by: Stefano Stabellini Reviewed-by: Paul Durrant --- hw/xen/xen-hvm-common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index cb82f4b83d..42339c96bd 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -33,7 +33,7 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, trace_xen_ram_alloc(ram_addr, size); nr_pfn = size >> TARGET_PAGE_BITS; - pfn_list = g_malloc(sizeof (*pfn_list) * nr_pfn); + pfn_list = g_new(xen_pfn_t, nr_pfn); for (i = 0; i < nr_pfn; i++) { pfn_list[i] = (ram_addr >> TARGET_PAGE_BITS) + i; @@ -730,7 +730,7 @@ void destroy_hvm_domain(bool reboot) return; } if (errno != ENOTTY /* old Xen */) { - perror("xendevicemodel_shutdown failed"); + error_report("xendevicemodel_shutdown failed with error %d", errno); } /* well, try the old thing then */ } @@ -784,7 +784,7 @@ static void xen_do_ioreq_register(XenIOState *state, } /* Note: cpus is empty at this point in init */ - state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); + state->cpu_by_vcpu_id = g_new0(CPUState *, max_cpus); rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); if (rc < 0) { @@ -793,7 +793,7 @@ static void xen_do_ioreq_register(XenIOState *state, goto err; } - state->ioreq_local_port = g_malloc0(max_cpus * sizeof (evtchn_port_t)); + state->ioreq_local_port = g_new0(evtchn_port_t, max_cpus); /* FIXME: how about if we overflow the page here? */ for (i = 0; i < max_cpus; i++) { @@ -850,13 +850,13 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, state->xce_handle = qemu_xen_evtchn_open(); if (state->xce_handle == NULL) { - perror("xen: event channel open"); + error_report("xen: event channel open failed with error %d", errno); goto err; } state->xenstore = xs_daemon_open(); if (state->xenstore == NULL) { - perror("xen: xenstore open"); + error_report("xen: xenstore open failed with error %d", errno); goto err; } From a4b4b40be86f69a994bf614edd49d8f1fb79422e Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 14 Jun 2023 17:03:36 -0700 Subject: [PATCH 353/412] meson.build: do not set have_xen_pci_passthrough for aarch64 targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit have_xen_pci_passthrough is only used for Xen x86 VMs. Signed-off-by: Stefano Stabellini Reviewed-by: Alex Bennée --- meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meson.build b/meson.build index 34306a6205..481865bfa9 100644 --- a/meson.build +++ b/meson.build @@ -1726,6 +1726,8 @@ have_xen_pci_passthrough = get_option('xen_pci_passthrough') \ error_message: 'Xen PCI passthrough requested but Xen not enabled') \ .require(targetos == 'linux', error_message: 'Xen PCI passthrough not available on this platform') \ + .require(cpu == 'x86' or cpu == 'x86_64', + error_message: 'Xen PCI passthrough not available on this platform') \ .allowed() From 733766cd37338ea08cb6c22c6e040d55b26f326c Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 14 Jun 2023 17:03:37 -0700 Subject: [PATCH 354/412] hw/arm: introduce xenpvh machine Add a new machine xenpvh which creates a IOREQ server to register/connect with Xen Hypervisor. Optional: When CONFIG_TPM is enabled, it also creates a tpm-tis-device, adds a TPM emulator and connects to swtpm running on host machine via chardev socket and support TPM functionalities for a guest domain. Extra command line for aarch64 xenpvh QEMU to connect to swtpm: -chardev socket,id=chrtpm,path=/tmp/myvtpm2/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ -machine tpm-base-addr=0x0c000000 \ swtpm implements a TPM software emulator(TPM 1.2 & TPM 2) built on libtpms and provides access to TPM functionality over socket, chardev and CUSE interface. Github repo: https://github.com/stefanberger/swtpm Example for starting swtpm on host machine: mkdir /tmp/vtpm2 swtpm socket --tpmstate dir=/tmp/vtpm2 \ --ctrl type=unixio,path=/tmp/vtpm2/swtpm-sock & Signed-off-by: Vikram Garhwal Signed-off-by: Stefano Stabellini Reviewed-by: Stefano Stabellini --- docs/system/arm/xenpvh.rst | 34 +++++++ docs/system/target-arm.rst | 1 + hw/arm/meson.build | 2 + hw/arm/xen_arm.c | 181 ++++++++++++++++++++++++++++++++++ include/hw/arm/xen_arch_hvm.h | 9 ++ include/hw/xen/arch_hvm.h | 2 + 6 files changed, 229 insertions(+) create mode 100644 docs/system/arm/xenpvh.rst create mode 100644 hw/arm/xen_arm.c create mode 100644 include/hw/arm/xen_arch_hvm.h diff --git a/docs/system/arm/xenpvh.rst b/docs/system/arm/xenpvh.rst new file mode 100644 index 0000000000..e1655c7ab8 --- /dev/null +++ b/docs/system/arm/xenpvh.rst @@ -0,0 +1,34 @@ +XENPVH (``xenpvh``) +========================================= +This machine creates a IOREQ server to register/connect with Xen Hypervisor. + +When TPM is enabled, this machine also creates a tpm-tis-device at a user input +tpm base address, adds a TPM emulator and connects to a swtpm application +running on host machine via chardev socket. This enables xenpvh to support TPM +functionalities for a guest domain. + +More information about TPM use and installing swtpm linux application can be +found at: docs/specs/tpm.rst. + +Example for starting swtpm on host machine: +.. code-block:: console + + mkdir /tmp/vtpm2 + swtpm socket --tpmstate dir=/tmp/vtpm2 \ + --ctrl type=unixio,path=/tmp/vtpm2/swtpm-sock & + +Sample QEMU xenpvh commands for running and connecting with Xen: +.. code-block:: console + + qemu-system-aarch64 -xen-domid 1 \ + -chardev socket,id=libxl-cmd,path=qmp-libxl-1,server=on,wait=off \ + -mon chardev=libxl-cmd,mode=control \ + -chardev socket,id=libxenstat-cmd,path=qmp-libxenstat-1,server=on,wait=off \ + -mon chardev=libxenstat-cmd,mode=control \ + -xen-attach -name guest0 -vnc none -display none -nographic \ + -machine xenpvh -m 1301 \ + -chardev socket,id=chrtpm,path=tmp/vtpm2/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm -machine tpm-base-addr=0x0C000000 + +In above QEMU command, last two lines are for connecting xenpvh QEMU to swtpm +via chardev socket. diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index a12b6bca05..790ac1b8a2 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -107,6 +107,7 @@ undocumented; you can get a complete list by running arm/stm32 arm/virt arm/xlnx-versal-virt + arm/xenpvh Emulated CPU architecture support ================================= diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 870ec67376..4f94f821b0 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -63,6 +63,8 @@ arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre. arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) +arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c')) +arm_ss.add_all(xen_ss) softmmu_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) diff --git a/hw/arm/xen_arm.c b/hw/arm/xen_arm.c new file mode 100644 index 0000000000..19b1cb81ad --- /dev/null +++ b/hw/arm/xen_arm.c @@ -0,0 +1,181 @@ +/* + * QEMU ARM Xen PVH Machine + * + * + * 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 "qemu/error-report.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/visitor.h" +#include "hw/boards.h" +#include "hw/sysbus.h" +#include "sysemu/block-backend.h" +#include "sysemu/tpm_backend.h" +#include "sysemu/sysemu.h" +#include "hw/xen/xen-hvm-common.h" +#include "sysemu/tpm.h" +#include "hw/xen/arch_hvm.h" + +#define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh") +OBJECT_DECLARE_SIMPLE_TYPE(XenArmState, XEN_ARM) + +static MemoryListener xen_memory_listener = { + .region_add = xen_region_add, + .region_del = xen_region_del, + .log_start = NULL, + .log_stop = NULL, + .log_sync = NULL, + .log_global_start = NULL, + .log_global_stop = NULL, + .priority = 10, +}; + +struct XenArmState { + /*< private >*/ + MachineState parent; + + XenIOState *state; + + struct { + uint64_t tpm_base_addr; + } cfg; +}; + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req) +{ + hw_error("Invalid ioreq type 0x%x\n", req->type); + + return; +} + +void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, + bool add) +{ +} + +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) +{ +} + +void qmp_xen_set_global_dirty_log(bool enable, Error **errp) +{ +} + +#ifdef CONFIG_TPM +static void xen_enable_tpm(XenArmState *xam) +{ + Error *errp = NULL; + DeviceState *dev; + SysBusDevice *busdev; + + TPMBackend *be = qemu_find_tpm_be("tpm0"); + if (be == NULL) { + DPRINTF("Couldn't fine the backend for tpm0\n"); + return; + } + dev = qdev_new(TYPE_TPM_TIS_SYSBUS); + object_property_set_link(OBJECT(dev), "tpmdev", OBJECT(be), &errp); + object_property_set_str(OBJECT(dev), "tpmdev", be->id, &errp); + busdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(busdev, &error_fatal); + sysbus_mmio_map(busdev, 0, xam->cfg.tpm_base_addr); + + DPRINTF("Connected tpmdev at address 0x%lx\n", xam->cfg.tpm_base_addr); +} +#endif + +static void xen_arm_init(MachineState *machine) +{ + XenArmState *xam = XEN_ARM(machine); + + xam->state = g_new0(XenIOState, 1); + + xen_register_ioreq(xam->state, machine->smp.cpus, xen_memory_listener); + +#ifdef CONFIG_TPM + if (xam->cfg.tpm_base_addr) { + xen_enable_tpm(xam); + } else { + DPRINTF("tpm-base-addr is not provided. TPM will not be enabled\n"); + } +#endif +} + +#ifdef CONFIG_TPM +static void xen_arm_get_tpm_base_addr(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + XenArmState *xam = XEN_ARM(obj); + uint64_t value = xam->cfg.tpm_base_addr; + + visit_type_uint64(v, name, &value, errp); +} + +static void xen_arm_set_tpm_base_addr(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + XenArmState *xam = XEN_ARM(obj); + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + xam->cfg.tpm_base_addr = value; +} +#endif + +static void xen_arm_machine_class_init(ObjectClass *oc, void *data) +{ + + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Xen Para-virtualized PC"; + mc->init = xen_arm_init; + mc->max_cpus = 1; + mc->default_machine_opts = "accel=xen"; + +#ifdef CONFIG_TPM + object_class_property_add(oc, "tpm-base-addr", "uint64_t", + xen_arm_get_tpm_base_addr, + xen_arm_set_tpm_base_addr, + NULL, NULL); + object_class_property_set_description(oc, "tpm-base-addr", + "Set Base address for TPM device."); + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); +#endif +} + +static const TypeInfo xen_arm_machine_type = { + .name = TYPE_XEN_ARM, + .parent = TYPE_MACHINE, + .class_init = xen_arm_machine_class_init, + .instance_size = sizeof(XenArmState), +}; + +static void xen_arm_machine_register_types(void) +{ + type_register_static(&xen_arm_machine_type); +} + +type_init(xen_arm_machine_register_types) diff --git a/include/hw/arm/xen_arch_hvm.h b/include/hw/arm/xen_arch_hvm.h new file mode 100644 index 0000000000..8fd645e723 --- /dev/null +++ b/include/hw/arm/xen_arch_hvm.h @@ -0,0 +1,9 @@ +#ifndef HW_XEN_ARCH_ARM_HVM_H +#define HW_XEN_ARCH_ARM_HVM_H + +#include +void arch_handle_ioreq(XenIOState *state, ioreq_t *req); +void arch_xen_set_memory(XenIOState *state, + MemoryRegionSection *section, + bool add); +#endif diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h index 26674648d8..c7c515220d 100644 --- a/include/hw/xen/arch_hvm.h +++ b/include/hw/xen/arch_hvm.h @@ -1,3 +1,5 @@ #if defined(TARGET_I386) || defined(TARGET_X86_64) #include "hw/i386/xen_arch_hvm.h" +#elif defined(TARGET_ARM) || defined(TARGET_ARM_64) +#include "hw/arm/xen_arch_hvm.h" #endif From aaea616d54317b8a0154adf52303a51da2d8d56f Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 14 Jun 2023 17:03:38 -0700 Subject: [PATCH 355/412] meson.build: enable xenpv machine build for ARM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CONFIG_XEN for aarch64 device to support build for ARM targets. Signed-off-by: Vikram Garhwal Signed-off-by: Stefano Stabellini Reviewed-by: Alex Bennée --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 481865bfa9..cfa98e9e25 100644 --- a/meson.build +++ b/meson.build @@ -136,7 +136,7 @@ endif if cpu in ['x86', 'x86_64', 'arm', 'aarch64'] # i386 emulator provides xenpv machine type for multiple architectures accelerator_targets += { - 'CONFIG_XEN': ['i386-softmmu', 'x86_64-softmmu'], + 'CONFIG_XEN': ['i386-softmmu', 'x86_64-softmmu', 'aarch64-softmmu'], } endif if cpu in ['x86', 'x86_64'] From d8a714eba68cd7221d44a6acb6b8a69cf6f2f86b Mon Sep 17 00:00:00 2001 From: Vikram Garhwal Date: Wed, 14 Jun 2023 17:03:39 -0700 Subject: [PATCH 356/412] test/qtest: add xepvh to skip list for qtest Like existing xen machines, xenpvh also cannot be used for qtest. Signed-off-by: Vikram Garhwal Reviewed-by: Stefano Stabellini --- tests/qtest/libqtest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 77de16227f..de03ef5f60 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1465,7 +1465,8 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), for (i = 0; machines[i].name != NULL; i++) { /* Ignore machines that cannot be used for qtests */ if (!strncmp("xenfv", machines[i].name, 5) || - g_str_equal("xenpv", machines[i].name)) { + g_str_equal("xenpv", machines[i].name) || + g_str_equal("xenpvh", machines[i].name)) { continue; } if (!skip_old_versioned || From 8f30771ce61b0290fdb00b4cdbbc514723008a12 Mon Sep 17 00:00:00 2001 From: Tianrui Zhao Date: Tue, 16 May 2023 16:27:57 +0800 Subject: [PATCH 357/412] hw/loongarch/virt: Add cpu arch_id support With acpi madt table, there is cpu physical coreid, which may be different with logical id in qemu. This patch adds cpu arch_id support, and fill madt table with arch_id. For the present cpu arch_id is still equal to logical id. Reviewed-by: Song Gao Signed-off-by: Tianrui Zhao Signed-off-by: Song Gao Message-Id: <20230613120552.2471420-2-zhaotianrui@loongson.cn> --- hw/loongarch/acpi-build.c | 20 ++++++++++++++------ hw/loongarch/virt.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 8e3ce07367..232344e1c7 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -107,7 +107,9 @@ static void build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) { MachineState *ms = MACHINE(lams); - int i; + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + int i, arch_id; AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id, .oem_table_id = lams->oem_table_id }; @@ -117,13 +119,15 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) build_append_int_noprefix(table_data, 0, 4); build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ - for (i = 0; i < ms->smp.cpus; i++) { + for (i = 0; i < arch_ids->len; i++) { /* Processor Core Interrupt Controller Structure */ + arch_id = arch_ids->cpus[i].arch_id; + build_append_int_noprefix(table_data, 17, 1); /* Type */ build_append_int_noprefix(table_data, 15, 1); /* Length */ build_append_int_noprefix(table_data, 1, 1); /* Version */ build_append_int_noprefix(table_data, i + 1, 4); /* ACPI Processor ID */ - build_append_int_noprefix(table_data, i, 4); /* Core ID */ + build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ build_append_int_noprefix(table_data, 1, 4); /* Flags */ } @@ -159,9 +163,11 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { - uint64_t i; + int i, arch_id; LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); MachineState *ms = MACHINE(lams); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, .oem_table_id = lams->oem_table_id }; @@ -169,13 +175,15 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_append_int_noprefix(table_data, 1, 4); /* Reserved */ build_append_int_noprefix(table_data, 0, 8); /* Reserved */ - for (i = 0; i < ms->smp.cpus; ++i) { + for (i = 0; i < arch_ids->len; ++i) { + arch_id = arch_ids->cpus[i].arch_id; + /* Processor Local APIC/SAPIC Affinity Structure */ build_append_int_noprefix(table_data, 0, 1); /* Type */ build_append_int_noprefix(table_data, 16, 1); /* Length */ /* Proximity Domain [7:0] */ build_append_int_noprefix(table_data, 0, 1); - build_append_int_noprefix(table_data, i, 1); /* APIC ID */ + build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */ /* Flags, Table 5-36 */ build_append_int_noprefix(table_data, 1, 4); build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */ diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index ceddec1b23..ced5a862f8 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -771,6 +771,9 @@ static void loongarch_init(MachineState *machine) LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); int i; hwaddr fdt_base; + const CPUArchIdList *possible_cpus; + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUState *cpu; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -787,8 +790,12 @@ static void loongarch_init(MachineState *machine) } create_fdt(lams); /* Init CPUs */ - for (i = 0; i < machine->smp.cpus; i++) { - cpu_create(machine->cpu_type); + + possible_cpus = mc->possible_cpu_arch_ids(machine); + for (i = 0; i < possible_cpus->len; i++) { + cpu = cpu_create(machine->cpu_type); + cpu->cpu_index = i; + machine->possible_cpus->cpus[i].cpu = OBJECT(cpu); } fdt_add_cpu_nodes(lams); /* Add memory region */ @@ -1022,6 +1029,28 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, return NULL; } +static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) +{ + int n; + unsigned int max_cpus = ms->smp.max_cpus; + + if (ms->possible_cpus) { + 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 (n = 0; n < ms->possible_cpus->len; n++) { + ms->possible_cpus->cpus[n].type = ms->cpu_type; + ms->possible_cpus->cpus[n].arch_id = n; + ms->possible_cpus->cpus[n].props.has_core_id = true; + ms->possible_cpus->cpus[n].props.core_id = n % ms->smp.cores; + } + return ms->possible_cpus; +} + static void loongarch_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1038,6 +1067,7 @@ static void loongarch_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; mc->no_cdrom = 1; + mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; mc->get_hotplug_handler = virt_machine_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; hc->plug = loongarch_machine_device_plug_cb; From 758a7475663f36d0d411e9cb4199b6c543152f8a Mon Sep 17 00:00:00 2001 From: Tianrui Zhao Date: Wed, 17 May 2023 09:22:00 +0800 Subject: [PATCH 358/412] hw/intc: Set physical cpuid route for LoongArch ipi device LoongArch ipi device uses physical cpuid to route to different vcpus rather logical cpuid, and the physical cpuid is the same with cpuid in acpi dsdt and srat table. Reviewed-by: Song Gao Signed-off-by: Tianrui Zhao Signed-off-by: Song Gao Message-Id: <20230613120552.2471420-3-zhaotianrui@loongson.cn> --- hw/intc/loongarch_ipi.c | 44 ++++++++++++++++++++++++++++++++++------- hw/loongarch/virt.c | 1 + target/loongarch/cpu.h | 2 ++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 3e45381652..67858b521c 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -17,6 +17,8 @@ #include "target/loongarch/internals.h" #include "trace.h" +static void loongarch_ipi_writel(void *, hwaddr, uint64_t, unsigned); + static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) { IPICore *s = opaque; @@ -75,13 +77,42 @@ static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr) data, MEMTXATTRS_UNSPECIFIED, NULL); } +static int archid_cmp(const void *a, const void *b) +{ + CPUArchId *archid_a = (CPUArchId *)a; + CPUArchId *archid_b = (CPUArchId *)b; + + return archid_a->arch_id - archid_b->arch_id; +} + +static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) +{ + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = id; + found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, + ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), + archid_cmp); + + return found_cpu; +} + +static CPUState *ipi_getcpu(int arch_id) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + CPUArchId *archid; + + archid = find_cpu_by_archid(machine, arch_id); + return CPU(archid->cpu); +} + static void ipi_send(uint64_t val) { uint32_t cpuid; uint8_t vector; - CPULoongArchState *env; CPUState *cs; LoongArchCPU *cpu; + LoongArchIPI *s; cpuid = extract32(val, 16, 10); if (cpuid >= LOONGARCH_MAX_CPUS) { @@ -92,11 +123,10 @@ static void ipi_send(uint64_t val) /* IPI status vector */ vector = extract8(val, 0, 5); - cs = qemu_get_cpu(cpuid); + cs = ipi_getcpu(cpuid); cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - address_space_stl(&env->address_space_iocsr, 0x1008, - BIT(vector), MEMTXATTRS_UNSPECIFIED, NULL); + s = LOONGARCH_IPI(cpu->env.ipistate); + loongarch_ipi_writel(&s->ipi_core, CORE_SET_OFF, BIT(vector), 4); } static void mail_send(uint64_t val) @@ -114,7 +144,7 @@ static void mail_send(uint64_t val) } addr = 0x1020 + (val & 0x1c); - cs = qemu_get_cpu(cpuid); + cs = ipi_getcpu(cpuid); cpu = LOONGARCH_CPU(cs); env = &cpu->env; send_ipi_data(env, val, addr); @@ -135,7 +165,7 @@ static void any_send(uint64_t val) } addr = val & 0xffff; - cs = qemu_get_cpu(cpuid); + cs = ipi_getcpu(cpuid); cpu = LOONGARCH_CPU(cs); env = &cpu->env; send_ipi_data(env, val, addr); diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index ced5a862f8..17bc37bccd 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -617,6 +617,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) memory_region_add_subregion(&env->system_iocsr, APIC_BASE, sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), cpu)); + env->ipistate = ipi; } /* diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 1f37e36b7c..b23f38c3d5 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -351,6 +351,8 @@ typedef struct CPUArchState { MemoryRegion iocsr_mem; bool load_elf; uint64_t elf_address; + /* Store ipistate to access from this struct */ + DeviceState *ipistate; #endif } CPULoongArchState; From 0cf1478d6d708276f37361169e327b755669cf8a Mon Sep 17 00:00:00 2001 From: Tianrui Zhao Date: Thu, 18 May 2023 14:58:15 +0800 Subject: [PATCH 359/412] hw/loongarch: Add numa support 1. Implement some functions for LoongArch numa support; 2. Implement fdt_add_memory_node() for fdt; 3. build_srat() fills node_id and adds build numa memory. Reviewed-by: Song Gao Signed-off-by: Tianrui Zhao Signed-off-by: Song Gao Message-Id: <20230613122613.2471743-1-zhaotianrui@loongson.cn> --- hw/loongarch/Kconfig | 1 + hw/loongarch/acpi-build.c | 58 ++++++++++++++++++---- hw/loongarch/virt.c | 102 +++++++++++++++++++++++++++++++++----- 3 files changed, 138 insertions(+), 23 deletions(-) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index eb112af990..1e7c5b43c5 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -21,3 +21,4 @@ config LOONGARCH_VIRT select FW_CFG_DMA select DIMM select PFLASH_CFI01 + select ACPI_HMAT diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 232344e1c7..f526f3abba 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -34,6 +34,7 @@ #include "sysemu/tpm.h" #include "hw/platform-bus.h" #include "hw/acpi/aml-build.h" +#include "hw/acpi/hmat.h" #define ACPI_BUILD_ALIGN_SIZE 0x1000 #define ACPI_BUILD_TABLE_SIZE 0x20000 @@ -163,11 +164,12 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { - int i, arch_id; + int i, arch_id, node_id; + uint64_t mem_len, mem_base; + int nb_numa_nodes = machine->numa_state->num_nodes; LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - MachineState *ms = MACHINE(lams); - MachineClass *mc = MACHINE_GET_CLASS(ms); - const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + MachineClass *mc = MACHINE_GET_CLASS(lams); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, .oem_table_id = lams->oem_table_id }; @@ -177,12 +179,13 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) for (i = 0; i < arch_ids->len; ++i) { arch_id = arch_ids->cpus[i].arch_id; + node_id = arch_ids->cpus[i].props.node_id; /* Processor Local APIC/SAPIC Affinity Structure */ build_append_int_noprefix(table_data, 0, 1); /* Type */ build_append_int_noprefix(table_data, 16, 1); /* Length */ /* Proximity Domain [7:0] */ - build_append_int_noprefix(table_data, 0, 1); + build_append_int_noprefix(table_data, node_id, 1); build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */ /* Flags, Table 5-36 */ build_append_int_noprefix(table_data, 1, 4); @@ -192,16 +195,36 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } + /* Node0 */ build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0, MEM_AFFINITY_ENABLED); + mem_base = VIRT_HIGHMEM_BASE; + if (!nb_numa_nodes) { + mem_len = machine->ram_size - VIRT_LOWMEM_SIZE; + } else { + mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE; + } + if (mem_len) + build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED); - build_srat_memory(table_data, VIRT_HIGHMEM_BASE, machine->ram_size - VIRT_LOWMEM_SIZE, - 0, MEM_AFFINITY_ENABLED); + /* Node1 - Nodemax */ + if (nb_numa_nodes) { + mem_base += mem_len; + for (i = 1; i < nb_numa_nodes; ++i) { + if (machine->numa_state->nodes[i].node_mem > 0) { + build_srat_memory(table_data, mem_base, + machine->numa_state->nodes[i].node_mem, i, + MEM_AFFINITY_ENABLED); + mem_base += machine->numa_state->nodes[i].node_mem; + } + } + } - if (ms->device_memory) { - build_srat_memory(table_data, ms->device_memory->base, - memory_region_size(&ms->device_memory->mr), - 0, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + if (machine->device_memory) { + build_srat_memory(table_data, machine->device_memory->base, + memory_region_size(&machine->device_memory->mr), + nb_numa_nodes - 1, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } acpi_table_end(linker, &table); @@ -417,6 +440,19 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); + if (machine->numa_state->num_nodes) { + if (machine->numa_state->have_numa_distance) { + acpi_add_table(table_offsets, tables_blob); + build_slit(tables_blob, tables->linker, machine, lams->oem_id, + lams->oem_table_id); + } + if (machine->numa_state->hmat_enabled) { + acpi_add_table(table_offsets, tables_blob); + build_hmat(tables_blob, tables->linker, machine->numa_state, + lams->oem_id, lams->oem_table_id); + } + } + acpi_add_table(table_offsets, tables_blob); { AcpiMcfgInfo mcfg = { diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 17bc37bccd..1d5c764408 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -164,11 +164,16 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) for (num = smp_cpus - 1; num >= 0; num--) { char *nodename = g_strdup_printf("/cpus/cpu@%d", num); LoongArchCPU *cpu = LOONGARCH_CPU(qemu_get_cpu(num)); + CPUState *cs = CPU(cpu); qemu_fdt_add_subnode(ms->fdt, nodename); qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", cpu->dtb_compatible); + if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", + ms->possible_cpus->cpus[cs->cpu_index].props.node_id); + } qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", qemu_fdt_alloc_phandle(ms->fdt)); @@ -280,6 +285,22 @@ static void fdt_add_irqchip_node(LoongArchMachineState *lams) g_free(nodename); } +static void fdt_add_memory_node(MachineState *ms, + uint64_t base, uint64_t size, int node_id) +{ + char *nodename = g_strdup_printf("/memory@%" PRIx64, base); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + + if (ms->numa_state && ms->numa_state->num_nodes) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); + } + + g_free(nodename); +} + #define PM_BASE 0x10080000 #define PM_SIZE 0x100 #define PM_CTRL 0x10 @@ -767,14 +788,17 @@ static void loongarch_init(MachineState *machine) const char *cpu_model = machine->cpu_type; ram_addr_t offset = 0; ram_addr_t ram_size = machine->ram_size; - uint64_t highram_size = 0; + uint64_t highram_size = 0, phyAddr = 0; MemoryRegion *address_space_mem = get_system_memory(); LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + int nb_numa_nodes = machine->numa_state->num_nodes; + NodeInfo *numa_info = machine->numa_state->nodes; int i; hwaddr fdt_base; const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); CPUState *cpu; + char *ramName = NULL; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -799,17 +823,43 @@ static void loongarch_init(MachineState *machine) machine->possible_cpus->cpus[i].cpu = OBJECT(cpu); } fdt_add_cpu_nodes(lams); - /* Add memory region */ - memory_region_init_alias(&lams->lowmem, NULL, "loongarch.lowram", - machine->ram, 0, 256 * MiB); - memory_region_add_subregion(address_space_mem, offset, &lams->lowmem); - offset += 256 * MiB; - memmap_add_entry(0, 256 * MiB, 1); - highram_size = ram_size - 256 * MiB; - memory_region_init_alias(&lams->highmem, NULL, "loongarch.highmem", - machine->ram, offset, highram_size); - memory_region_add_subregion(address_space_mem, 0x90000000, &lams->highmem); - memmap_add_entry(0x90000000, highram_size, 1); + + /* Node0 memory */ + memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); + fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0); + memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram", + machine->ram, offset, VIRT_LOWMEM_SIZE); + memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem); + + offset += VIRT_LOWMEM_SIZE; + if (nb_numa_nodes > 0) { + assert(numa_info[0].node_mem > VIRT_LOWMEM_SIZE); + highram_size = numa_info[0].node_mem - VIRT_LOWMEM_SIZE; + } else { + highram_size = ram_size - VIRT_LOWMEM_SIZE; + } + phyAddr = VIRT_HIGHMEM_BASE; + memmap_add_entry(phyAddr, highram_size, 1); + fdt_add_memory_node(machine, phyAddr, highram_size, 0); + memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram", + machine->ram, offset, highram_size); + memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem); + + /* Node1 - Nodemax memory */ + offset += highram_size; + phyAddr += highram_size; + + for (i = 1; i < nb_numa_nodes; i++) { + MemoryRegion *nodemem = g_new(MemoryRegion, 1); + ramName = g_strdup_printf("loongarch.node%d.ram", i); + memory_region_init_alias(nodemem, NULL, ramName, machine->ram, + offset, numa_info[i].node_mem); + memory_region_add_subregion(address_space_mem, phyAddr, nodemem); + memmap_add_entry(phyAddr, numa_info[i].node_mem, 1); + fdt_add_memory_node(machine, phyAddr, numa_info[i].node_mem, i); + offset += numa_info[i].node_mem; + phyAddr += numa_info[i].node_mem; + } /* initialize device memory address space */ if (machine->ram_size < machine->maxram_size) { @@ -1052,6 +1102,29 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } +static CpuInstanceProperties +virt_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 virt_get_default_cpu_node_id(const MachineState *ms, int idx) +{ + int64_t nidx = 0; + + if (ms->numa_state->num_nodes) { + nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); + if (ms->numa_state->num_nodes <= nidx) { + nidx = ms->numa_state->num_nodes - 1; + } + } + return nidx; +} + static void loongarch_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1069,6 +1142,11 @@ static void loongarch_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->no_cdrom = 1; mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; + mc->cpu_index_to_instance_props = virt_cpu_index_to_props; + mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; + mc->numa_mem_supported = true; + mc->auto_enable_numa_with_memhp = true; + mc->auto_enable_numa_with_memdev = true; mc->get_hotplug_handler = virt_machine_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; hc->plug = loongarch_machine_device_plug_cb; From f33238836544cb6f3b27968d1ececbd46f037904 Mon Sep 17 00:00:00 2001 From: Tianrui Zhao Date: Tue, 13 Jun 2023 19:37:36 +0800 Subject: [PATCH 360/412] hw/loongarch: Supplement cpu topology arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supplement LoongArch cpu topology arguments, including support socket and threads per core. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Song Gao Signed-off-by: Tianrui Zhao Signed-off-by: Song Gao Message-Id: <20230613123251.2471878-1-zhaotianrui@loongson.cn> --- hw/loongarch/acpi-build.c | 4 ++++ hw/loongarch/virt.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index f526f3abba..0b62c3a2f7 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -437,6 +437,10 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_madt(tables_blob, tables->linker, lams); + acpi_add_table(table_offsets, tables_blob); + build_pptt(tables_blob, tables->linker, machine, + lams->oem_id, lams->oem_table_id); + acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 1d5c764408..ca8824b6ef 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1096,8 +1096,15 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) for (n = 0; n < ms->possible_cpus->len; n++) { ms->possible_cpus->cpus[n].type = ms->cpu_type; ms->possible_cpus->cpus[n].arch_id = n; + + ms->possible_cpus->cpus[n].props.has_socket_id = true; + ms->possible_cpus->cpus[n].props.socket_id = + n / (ms->smp.cores * ms->smp.threads); ms->possible_cpus->cpus[n].props.has_core_id = true; - ms->possible_cpus->cpus[n].props.core_id = n % ms->smp.cores; + ms->possible_cpus->cpus[n].props.core_id = + n / ms->smp.threads % ms->smp.cores; + ms->possible_cpus->cpus[n].props.has_thread_id = true; + ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads; } return ms->possible_cpus; } From 505aa8d8f29b79fcef77563bb4124208badbd8d4 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Wed, 14 Jun 2023 14:55:56 +0800 Subject: [PATCH 361/412] target/loongarch: Fix CSR.DMW0-3.VSEG check The previous code checks whether the highest 16 bits of virtual address equal to that of CSR.DMW0-3. This is incorrect according to the spec, and is corrected to compare only the highest four bits instead. Signed-off-by: Jiajie Chen Reviewed-by: Song Gao Message-Id: <20230614065556.2397513-1-c@jia.je> Signed-off-by: Song Gao --- target/loongarch/tlb_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c index cce1db1e0a..6e00190547 100644 --- a/target/loongarch/tlb_helper.c +++ b/target/loongarch/tlb_helper.c @@ -185,10 +185,10 @@ static int get_physical_address(CPULoongArchState *env, hwaddr *physical, } plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); - base_v = address >> TARGET_VIRT_ADDR_SPACE_BITS; + base_v = address >> R_CSR_DMW_VSEG_SHIFT; /* Check direct map window */ for (int i = 0; i < 4; i++) { - base_c = env->CSR_DMW[i] >> TARGET_VIRT_ADDR_SPACE_BITS; + base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW, VSEG); if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { *physical = dmw_va2pa(address); *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; From 243705aa6ea3465b20e9f5a8bfcf36d3153f3c10 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:18 +0100 Subject: [PATCH 362/412] target/arm: Fix return value from LDSMIN/LDSMAX 8/16 bit atomics The atomic memory operations are supposed to return the old memory data value in the destination register. This value is not sign-extended, even if the operation is the signed minimum or maximum. (In the pseudocode for the instructions the returned data value is passed to ZeroExtend() to create the value in the register.) We got this wrong because we were doing a 32-to-64 zero extend on the result for 8 and 16 bit data values, rather than the correct amount of zero extension. Fix the bug by using ext8u and ext16u for the MO_8 and MO_16 data sizes rather than ext32u. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-2-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index aa93f37e21..246e3c1514 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3545,8 +3545,22 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, */ fn(tcg_rt, clean_addr, tcg_rs, get_mem_index(s), mop); - if ((mop & MO_SIGN) && size != MO_64) { - tcg_gen_ext32u_i64(tcg_rt, tcg_rt); + if (mop & MO_SIGN) { + switch (size) { + case MO_8: + tcg_gen_ext8u_i64(tcg_rt, tcg_rt); + break; + case MO_16: + tcg_gen_ext16u_i64(tcg_rt, tcg_rt); + break; + case MO_32: + tcg_gen_ext32u_i64(tcg_rt, tcg_rt); + break; + case MO_64: + break; + default: + g_assert_not_reached(); + } } } From 7e2788471f9e079fff696a694721a7d41a451839 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:18 +0100 Subject: [PATCH 363/412] target/arm: Return correct result for LDG when ATA=0 The LDG instruction loads the tag from a memory address (identified by [Xn + offset]), and then merges that tag into the destination register Xt. We implemented this correctly for the case when allocation tags are enabled, but didn't get it right when ATA=0: instead of merging the tag bits into Xt, we merged them into the memory address [Xn + offset] and then set Xt to that. Merge the tag bits into the old Xt value, as they should be. Cc: qemu-stable@nongnu.org Fixes: c15294c1e36a7dd9b25 ("target/arm: Implement LDG, STG, ST2G instructions") Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 246e3c1514..4ec857bcd8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4201,9 +4201,13 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) if (s->ata) { gen_helper_ldg(tcg_rt, cpu_env, addr, tcg_rt); } else { + /* + * Tag access disabled: we must check for aborts on the load + * load from [rn+offset], and then insert a 0 tag into rt. + */ clean_addr = clean_data_tbi(s, addr); gen_probe_access(s, clean_addr, MMU_DATA_LOAD, MO_8); - gen_address_with_allocation_tag0(tcg_rt, addr); + gen_address_with_allocation_tag0(tcg_rt, tcg_rt); } } else { tcg_rt = cpu_reg_sp(s, rt); From 99bb43c0ff7c72ef7fa5035ff8e6151b1c08ce68 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:19 +0100 Subject: [PATCH 364/412] target/arm: Pass memop to gen_mte_check1_mmuidx() in reg_imm9 decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In disas_ldst_reg_imm9() we missed one place where a call to a gen_mte_check* function should now be passed the memop we have created rather than just being passed the size. Fix this. Fixes: 0a9091424d ("target/arm: Pass memop to gen_mte_check1*") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- target/arm/tcg/translate-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 4ec857bcd8..d271449431 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3226,7 +3226,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, clean_addr = gen_mte_check1_mmuidx(s, dirty_addr, is_store, writeback || rn != 31, - size, is_unpriv, memidx); + memop, is_unpriv, memidx); if (is_vector) { if (is_store) { From 68496d4172296ff04b90e3dfbee13316615d8167 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:19 +0100 Subject: [PATCH 365/412] target/arm: Consistently use finalize_memop_asimd() for ASIMD loads/stores In the recent refactoring we missed a few places which should be calling finalize_memop_asimd() for ASIMD loads and stores but instead are just calling finalize_memop(); fix these. For the disas_ldst_single_struct() and disas_ldst_multiple_struct() cases, this is not a behaviour change because there the size is never MO_128 and the two finalize functions do the same thing. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d271449431..1108f8287b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3309,6 +3309,7 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, if (!fp_access_check(s)) { return; } + memop = finalize_memop_asimd(s, size); } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ @@ -3321,6 +3322,7 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, is_store = (opc == 0); is_signed = !is_store && extract32(opc, 1, 1); is_extended = (size < 3) && extract32(opc, 0, 1); + memop = finalize_memop(s, size + is_signed * MO_SIGN); } if (rn == 31) { @@ -3333,7 +3335,6 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, tcg_gen_add_i64(dirty_addr, dirty_addr, tcg_rm); - memop = finalize_memop(s, size + is_signed * MO_SIGN); clean_addr = gen_mte_check1(s, dirty_addr, is_store, true, memop); if (is_vector) { @@ -3398,6 +3399,7 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, if (!fp_access_check(s)) { return; } + memop = finalize_memop_asimd(s, size); } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ @@ -3410,6 +3412,7 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, is_store = (opc == 0); is_signed = !is_store && extract32(opc, 1, 1); is_extended = (size < 3) && extract32(opc, 0, 1); + memop = finalize_memop(s, size + is_signed * MO_SIGN); } if (rn == 31) { @@ -3419,7 +3422,6 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, offset = imm12 << size; tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - memop = finalize_memop(s, size + is_signed * MO_SIGN); clean_addr = gen_mte_check1(s, dirty_addr, is_store, rn != 31, memop); if (is_vector) { @@ -3861,7 +3863,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) * promote consecutive little-endian elements below. */ clean_addr = gen_mte_checkN(s, tcg_rn, is_store, is_postidx || rn != 31, - total, finalize_memop(s, size)); + total, finalize_memop_asimd(s, size)); /* * Consecutive little-endian elements from a single register @@ -4019,7 +4021,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) total = selem << scale; tcg_rn = cpu_reg_sp(s, rn); - mop = finalize_memop(s, scale); + mop = finalize_memop_asimd(s, scale); clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31, total, mop); From 7fefc70661a70ba512a10ebefda0c9c29454d1e1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:19 +0100 Subject: [PATCH 366/412] target/arm: Convert hint instruction space to decodetree Convert the various instructions in the hint instruction space to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-3-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 31 ++++ target/arm/tcg/translate-a64.c | 277 ++++++++++++++++++--------------- 2 files changed, 185 insertions(+), 123 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 12a310d0a3..1efd436e17 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -150,3 +150,34 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB # the processor is in halting debug state (which we don't implement). # The pattern is listed here as documentation. # DRPS 1101011 0101 11111 000000 11111 00000 + +# Hint instruction group +{ + [ + YIELD 1101 0101 0000 0011 0010 0000 001 11111 + WFE 1101 0101 0000 0011 0010 0000 010 11111 + WFI 1101 0101 0000 0011 0010 0000 011 11111 + # We implement WFE to never block, so our SEV/SEVL are NOPs + # SEV 1101 0101 0000 0011 0010 0000 100 11111 + # SEVL 1101 0101 0000 0011 0010 0000 101 11111 + # Our DGL is a NOP because we don't merge memory accesses anyway. + # DGL 1101 0101 0000 0011 0010 0000 110 11111 + XPACLRI 1101 0101 0000 0011 0010 0000 111 11111 + PACIA1716 1101 0101 0000 0011 0010 0001 000 11111 + PACIB1716 1101 0101 0000 0011 0010 0001 010 11111 + AUTIA1716 1101 0101 0000 0011 0010 0001 100 11111 + AUTIB1716 1101 0101 0000 0011 0010 0001 110 11111 + ESB 1101 0101 0000 0011 0010 0010 000 11111 + PACIAZ 1101 0101 0000 0011 0010 0011 000 11111 + PACIASP 1101 0101 0000 0011 0010 0011 001 11111 + PACIBZ 1101 0101 0000 0011 0010 0011 010 11111 + PACIBSP 1101 0101 0000 0011 0010 0011 011 11111 + AUTIAZ 1101 0101 0000 0011 0010 0011 100 11111 + AUTIASP 1101 0101 0000 0011 0010 0011 101 11111 + AUTIBZ 1101 0101 0000 0011 0010 0011 110 11111 + AUTIBSP 1101 0101 0000 0011 0010 0011 111 11111 + ] + # The canonical NOP has CRm == op2 == 0, but all of the space + # that isn't specifically allocated to an instruction must NOP + NOP 1101 0101 0000 0011 0010 ---- --- 11111 +} diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1108f8287b..eb8addac1b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1649,133 +1649,167 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a) return true; } -/* HINT instruction group, including various allocated HINTs */ -static void handle_hint(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) +static bool trans_NOP(DisasContext *s, arg_NOP *a) { - unsigned int selector = crm << 3 | op2; + return true; +} - if (op1 != 3) { - unallocated_encoding(s); - return; +static bool trans_YIELD(DisasContext *s, arg_YIELD *a) +{ + /* + * When running in MTTCG we don't generate jumps to the yield and + * WFE helpers as it won't affect the scheduling of other vCPUs. + * If we wanted to more completely model WFE/SEV so we don't busy + * spin unnecessarily we would need to do something more involved. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + s->base.is_jmp = DISAS_YIELD; } + return true; +} - switch (selector) { - case 0b00000: /* NOP */ - break; - case 0b00011: /* WFI */ - s->base.is_jmp = DISAS_WFI; - break; - case 0b00001: /* YIELD */ - /* When running in MTTCG we don't generate jumps to the yield and - * WFE helpers as it won't affect the scheduling of other vCPUs. - * If we wanted to more completely model WFE/SEV so we don't busy - * spin unnecessarily we would need to do something more involved. +static bool trans_WFI(DisasContext *s, arg_WFI *a) +{ + s->base.is_jmp = DISAS_WFI; + return true; +} + +static bool trans_WFE(DisasContext *s, arg_WFI *a) +{ + /* + * When running in MTTCG we don't generate jumps to the yield and + * WFE helpers as it won't affect the scheduling of other vCPUs. + * If we wanted to more completely model WFE/SEV so we don't busy + * spin unnecessarily we would need to do something more involved. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + s->base.is_jmp = DISAS_WFE; + } + return true; +} + +static bool trans_XPACLRI(DisasContext *s, arg_XPACLRI *a) +{ + if (s->pauth_active) { + gen_helper_xpaci(cpu_X[30], cpu_env, cpu_X[30]); + } + return true; +} + +static bool trans_PACIA1716(DisasContext *s, arg_PACIA1716 *a) +{ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_PACIB1716(DisasContext *s, arg_PACIB1716 *a) +{ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_AUTIA1716(DisasContext *s, arg_AUTIA1716 *a) +{ + if (s->pauth_active) { + gen_helper_autia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_AUTIB1716(DisasContext *s, arg_AUTIB1716 *a) +{ + if (s->pauth_active) { + gen_helper_autib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_ESB(DisasContext *s, arg_ESB *a) +{ + /* Without RAS, we must implement this as NOP. */ + if (dc_isar_feature(aa64_ras, s)) { + /* + * QEMU does not have a source of physical SErrors, + * so we are only concerned with virtual SErrors. + * The pseudocode in the ARM for this case is + * if PSTATE.EL IN {EL0, EL1} && EL2Enabled() then + * AArch64.vESBOperation(); + * Most of the condition can be evaluated at translation time. + * Test for EL2 present, and defer test for SEL2 to runtime. */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - s->base.is_jmp = DISAS_YIELD; + if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { + gen_helper_vesb(cpu_env); } - break; - case 0b00010: /* WFE */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - s->base.is_jmp = DISAS_WFE; - } - break; - case 0b00100: /* SEV */ - case 0b00101: /* SEVL */ - case 0b00110: /* DGH */ - /* we treat all as NOP at least for now */ - break; - case 0b00111: /* XPACLRI */ - if (s->pauth_active) { - gen_helper_xpaci(cpu_X[30], cpu_env, cpu_X[30]); - } - break; - case 0b01000: /* PACIA1716 */ - if (s->pauth_active) { - gen_helper_pacia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b01010: /* PACIB1716 */ - if (s->pauth_active) { - gen_helper_pacib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b01100: /* AUTIA1716 */ - if (s->pauth_active) { - gen_helper_autia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b01110: /* AUTIB1716 */ - if (s->pauth_active) { - gen_helper_autib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b10000: /* ESB */ - /* Without RAS, we must implement this as NOP. */ - if (dc_isar_feature(aa64_ras, s)) { - /* - * QEMU does not have a source of physical SErrors, - * so we are only concerned with virtual SErrors. - * The pseudocode in the ARM for this case is - * if PSTATE.EL IN {EL0, EL1} && EL2Enabled() then - * AArch64.vESBOperation(); - * Most of the condition can be evaluated at translation time. - * Test for EL2 present, and defer test for SEL2 to runtime. - */ - if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { - gen_helper_vesb(cpu_env); - } - } - break; - case 0b11000: /* PACIAZ */ - if (s->pauth_active) { - gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], - tcg_constant_i64(0)); - } - break; - case 0b11001: /* PACIASP */ - if (s->pauth_active) { - gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - case 0b11010: /* PACIBZ */ - if (s->pauth_active) { - gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], - tcg_constant_i64(0)); - } - break; - case 0b11011: /* PACIBSP */ - if (s->pauth_active) { - gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - case 0b11100: /* AUTIAZ */ - if (s->pauth_active) { - gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], - tcg_constant_i64(0)); - } - break; - case 0b11101: /* AUTIASP */ - if (s->pauth_active) { - gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - case 0b11110: /* AUTIBZ */ - if (s->pauth_active) { - gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], - tcg_constant_i64(0)); - } - break; - case 0b11111: /* AUTIBSP */ - if (s->pauth_active) { - gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - default: - /* default specified as NOP equivalent */ - break; } + return true; +} + +static bool trans_PACIAZ(DisasContext *s, arg_PACIAZ *a) +{ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_PACIASP(DisasContext *s, arg_PACIASP *a) +{ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_PACIBZ(DisasContext *s, arg_PACIBZ *a) +{ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_PACIBSP(DisasContext *s, arg_PACIBSP *a) +{ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_AUTIAZ(DisasContext *s, arg_AUTIAZ *a) +{ + if (s->pauth_active) { + gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_AUTIASP(DisasContext *s, arg_AUTIASP *a) +{ + if (s->pauth_active) { + gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_AUTIBZ(DisasContext *s, arg_AUTIBZ *a) +{ + if (s->pauth_active) { + gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_AUTIBSP(DisasContext *s, arg_AUTIBSP *a) +{ + if (s->pauth_active) { + gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + return true; } static void gen_clrex(DisasContext *s, uint32_t insn) @@ -2302,9 +2336,6 @@ static void disas_system(DisasContext *s, uint32_t insn) return; } switch (crn) { - case 2: /* HINT (including allocated hints like NOP, YIELD, etc) */ - handle_hint(s, insn, op1, op2, crm); - break; case 3: /* CLREX, DSB, DMB, ISB */ handle_sync(s, insn, op1, op2, crm); break; From afcd5df54c5bb9ffbfadf379cca4ecf20ef9b2dc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:20 +0100 Subject: [PATCH 367/412] target/arm: Convert barrier insns to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the insns in the "Barriers" instruction class to decodetree: CLREX, DSB, DMB, ISB and SB. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-4-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé --- target/arm/tcg/a64.decode | 7 +++ target/arm/tcg/translate-a64.c | 92 ++++++++++++++-------------------- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 1efd436e17..b3608d38dc 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -181,3 +181,10 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB # that isn't specifically allocated to an instruction must NOP NOP 1101 0101 0000 0011 0010 ---- --- 11111 } + +# Barriers + +CLREX 1101 0101 0000 0011 0011 ---- 010 11111 +DSB_DMB 1101 0101 0000 0011 0011 domain:2 types:2 10- 11111 +ISB 1101 0101 0000 0011 0011 ---- 110 11111 +SB 1101 0101 0000 0011 0011 0000 111 11111 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index eb8addac1b..088dfd8b1f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1812,67 +1812,56 @@ static bool trans_AUTIBSP(DisasContext *s, arg_AUTIBSP *a) return true; } -static void gen_clrex(DisasContext *s, uint32_t insn) +static bool trans_CLREX(DisasContext *s, arg_CLREX *a) { tcg_gen_movi_i64(cpu_exclusive_addr, -1); + return true; } -/* CLREX, DSB, DMB, ISB */ -static void handle_sync(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) +static bool trans_DSB_DMB(DisasContext *s, arg_DSB_DMB *a) { + /* We handle DSB and DMB the same way */ TCGBar bar; - if (op1 != 3) { - unallocated_encoding(s); - return; + switch (a->types) { + case 1: /* MBReqTypes_Reads */ + bar = TCG_BAR_SC | TCG_MO_LD_LD | TCG_MO_LD_ST; + break; + case 2: /* MBReqTypes_Writes */ + bar = TCG_BAR_SC | TCG_MO_ST_ST; + break; + default: /* MBReqTypes_All */ + bar = TCG_BAR_SC | TCG_MO_ALL; + break; } + tcg_gen_mb(bar); + return true; +} - switch (op2) { - case 2: /* CLREX */ - gen_clrex(s, insn); - return; - case 4: /* DSB */ - case 5: /* DMB */ - switch (crm & 3) { - case 1: /* MBReqTypes_Reads */ - bar = TCG_BAR_SC | TCG_MO_LD_LD | TCG_MO_LD_ST; - break; - case 2: /* MBReqTypes_Writes */ - bar = TCG_BAR_SC | TCG_MO_ST_ST; - break; - default: /* MBReqTypes_All */ - bar = TCG_BAR_SC | TCG_MO_ALL; - break; - } - tcg_gen_mb(bar); - return; - case 6: /* ISB */ - /* We need to break the TB after this insn to execute - * a self-modified code correctly and also to take - * any pending interrupts immediately. - */ - reset_btype(s); - gen_goto_tb(s, 0, 4); - return; +static bool trans_ISB(DisasContext *s, arg_ISB *a) +{ + /* + * We need to break the TB after this insn to execute + * self-modifying code correctly and also to take + * any pending interrupts immediately. + */ + reset_btype(s); + gen_goto_tb(s, 0, 4); + return true; +} - case 7: /* SB */ - if (crm != 0 || !dc_isar_feature(aa64_sb, s)) { - goto do_unallocated; - } - /* - * TODO: There is no speculation barrier opcode for TCG; - * MB and end the TB instead. - */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - gen_goto_tb(s, 0, 4); - return; - - default: - do_unallocated: - unallocated_encoding(s); - return; +static bool trans_SB(DisasContext *s, arg_SB *a) +{ + if (!dc_isar_feature(aa64_sb, s)) { + return false; } + /* + * TODO: There is no speculation barrier opcode for TCG; + * MB and end the TB instead. + */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + gen_goto_tb(s, 0, 4); + return true; } static void gen_xaflag(void) @@ -2336,9 +2325,6 @@ static void disas_system(DisasContext *s, uint32_t insn) return; } switch (crn) { - case 3: /* CLREX, DSB, DMB, ISB */ - handle_sync(s, insn, op1, op2, crm); - break; case 4: /* MSR (immediate) */ handle_msr_i(s, insn, op1, op2, crm); break; From d78b662f28ac6cdab6ad969bb23a10e0e3cf1c32 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:20 +0100 Subject: [PATCH 368/412] target/arm: Convert CFINV, XAFLAG and AXFLAG to decodetree Convert the CFINV, XAFLAG and AXFLAG insns to decodetree. The old decoder handles these in handle_msr_i(), but the architecture defines them as separate instructions from MSR (immediate). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-5-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 6 ++++ target/arm/tcg/translate-a64.c | 53 +++++++++++++++++----------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index b3608d38dc..fd23fc3e0f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -188,3 +188,9 @@ CLREX 1101 0101 0000 0011 0011 ---- 010 11111 DSB_DMB 1101 0101 0000 0011 0011 domain:2 types:2 10- 11111 ISB 1101 0101 0000 0011 0011 ---- 110 11111 SB 1101 0101 0000 0011 0011 0000 111 11111 + +# PSTATE + +CFINV 1101 0101 0000 0 000 0100 0000 000 11111 +XAFLAG 1101 0101 0000 0 000 0100 0000 001 11111 +AXFLAG 1101 0101 0000 0 000 0100 0000 010 11111 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 088dfd8b1f..c1b02b9618 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1864,9 +1864,24 @@ static bool trans_SB(DisasContext *s, arg_SB *a) return true; } -static void gen_xaflag(void) +static bool trans_CFINV(DisasContext *s, arg_CFINV *a) { - TCGv_i32 z = tcg_temp_new_i32(); + if (!dc_isar_feature(aa64_condm_4, s)) { + return false; + } + tcg_gen_xori_i32(cpu_CF, cpu_CF, 1); + return true; +} + +static bool trans_XAFLAG(DisasContext *s, arg_XAFLAG *a) +{ + TCGv_i32 z; + + if (!dc_isar_feature(aa64_condm_5, s)) { + return false; + } + + z = tcg_temp_new_i32(); tcg_gen_setcondi_i32(TCG_COND_EQ, z, cpu_ZF, 0); @@ -1890,10 +1905,16 @@ static void gen_xaflag(void) /* C | Z */ tcg_gen_or_i32(cpu_CF, cpu_CF, z); + + return true; } -static void gen_axflag(void) +static bool trans_AXFLAG(DisasContext *s, arg_AXFLAG *a) { + if (!dc_isar_feature(aa64_condm_5, s)) { + return false; + } + tcg_gen_sari_i32(cpu_VF, cpu_VF, 31); /* V ? -1 : 0 */ tcg_gen_andc_i32(cpu_CF, cpu_CF, cpu_VF); /* C & !V */ @@ -1902,6 +1923,8 @@ static void gen_axflag(void) tcg_gen_movi_i32(cpu_NF, 0); tcg_gen_movi_i32(cpu_VF, 0); + + return true; } /* MSR (immediate) - move immediate to processor state field */ @@ -1914,30 +1937,6 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, s->base.is_jmp = DISAS_TOO_MANY; switch (op) { - case 0x00: /* CFINV */ - if (crm != 0 || !dc_isar_feature(aa64_condm_4, s)) { - goto do_unallocated; - } - tcg_gen_xori_i32(cpu_CF, cpu_CF, 1); - s->base.is_jmp = DISAS_NEXT; - break; - - case 0x01: /* XAFlag */ - if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) { - goto do_unallocated; - } - gen_xaflag(); - s->base.is_jmp = DISAS_NEXT; - break; - - case 0x02: /* AXFlag */ - if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) { - goto do_unallocated; - } - gen_axflag(); - s->base.is_jmp = DISAS_NEXT; - break; - case 0x03: /* UAO */ if (!dc_isar_feature(aa64_uao, s) || s->current_el == 0) { goto do_unallocated; From 45d063d1630486491e22ace34a1d87d71c0eed35 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:20 +0100 Subject: [PATCH 369/412] target/arm: Convert MSR (immediate) to decodetree Convert the MSR (immediate) insn to decodetree. Our implementation has basically no commonality between the different destinations, so we decode the destination register in a64.decode. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-6-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 13 ++ target/arm/tcg/translate-a64.c | 251 ++++++++++++++++----------------- 2 files changed, 136 insertions(+), 128 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index fd23fc3e0f..4f94a08907 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -194,3 +194,16 @@ SB 1101 0101 0000 0011 0011 0000 111 11111 CFINV 1101 0101 0000 0 000 0100 0000 000 11111 XAFLAG 1101 0101 0000 0 000 0100 0000 001 11111 AXFLAG 1101 0101 0000 0 000 0100 0000 010 11111 + +# These are architecturally all "MSR (immediate)"; we decode the destination +# register too because there is no commonality in our implementation. +@msr_i .... .... .... . ... .... imm:4 ... ..... +MSR_i_UAO 1101 0101 0000 0 000 0100 .... 011 11111 @msr_i +MSR_i_PAN 1101 0101 0000 0 000 0100 .... 100 11111 @msr_i +MSR_i_SPSEL 1101 0101 0000 0 000 0100 .... 101 11111 @msr_i +MSR_i_SBSS 1101 0101 0000 0 011 0100 .... 001 11111 @msr_i +MSR_i_DIT 1101 0101 0000 0 011 0100 .... 010 11111 @msr_i +MSR_i_TCO 1101 0101 0000 0 011 0100 .... 100 11111 @msr_i +MSR_i_DAIFSET 1101 0101 0000 0 011 0100 .... 110 11111 @msr_i +MSR_i_DAIFCLEAR 1101 0101 0000 0 011 0100 .... 111 11111 @msr_i +MSR_i_SVCR 1101 0101 0000 0 011 0100 0 mask:2 imm:1 011 11111 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c1b02b9618..8c57b48d81 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1927,124 +1927,130 @@ static bool trans_AXFLAG(DisasContext *s, arg_AXFLAG *a) return true; } -/* MSR (immediate) - move immediate to processor state field */ -static void handle_msr_i(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) +static bool trans_MSR_i_UAO(DisasContext *s, arg_i *a) { - int op = op1 << 3 | op2; - - /* End the TB by default, chaining is ok. */ - s->base.is_jmp = DISAS_TOO_MANY; - - switch (op) { - case 0x03: /* UAO */ - if (!dc_isar_feature(aa64_uao, s) || s->current_el == 0) { - goto do_unallocated; - } - if (crm & 1) { - set_pstate_bits(PSTATE_UAO); - } else { - clear_pstate_bits(PSTATE_UAO); - } - gen_rebuild_hflags(s); - break; - - case 0x04: /* PAN */ - if (!dc_isar_feature(aa64_pan, s) || s->current_el == 0) { - goto do_unallocated; - } - if (crm & 1) { - set_pstate_bits(PSTATE_PAN); - } else { - clear_pstate_bits(PSTATE_PAN); - } - gen_rebuild_hflags(s); - break; - - case 0x05: /* SPSel */ - if (s->current_el == 0) { - goto do_unallocated; - } - gen_helper_msr_i_spsel(cpu_env, tcg_constant_i32(crm & PSTATE_SP)); - break; - - case 0x19: /* SSBS */ - if (!dc_isar_feature(aa64_ssbs, s)) { - goto do_unallocated; - } - if (crm & 1) { - set_pstate_bits(PSTATE_SSBS); - } else { - clear_pstate_bits(PSTATE_SSBS); - } - /* Don't need to rebuild hflags since SSBS is a nop */ - break; - - case 0x1a: /* DIT */ - if (!dc_isar_feature(aa64_dit, s)) { - goto do_unallocated; - } - if (crm & 1) { - set_pstate_bits(PSTATE_DIT); - } else { - clear_pstate_bits(PSTATE_DIT); - } - /* There's no need to rebuild hflags because DIT is a nop */ - break; - - case 0x1e: /* DAIFSet */ - gen_helper_msr_i_daifset(cpu_env, tcg_constant_i32(crm)); - break; - - case 0x1f: /* DAIFClear */ - gen_helper_msr_i_daifclear(cpu_env, tcg_constant_i32(crm)); - /* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs. */ - s->base.is_jmp = DISAS_UPDATE_EXIT; - break; - - case 0x1c: /* TCO */ - if (dc_isar_feature(aa64_mte, s)) { - /* Full MTE is enabled -- set the TCO bit as directed. */ - if (crm & 1) { - set_pstate_bits(PSTATE_TCO); - } else { - clear_pstate_bits(PSTATE_TCO); - } - gen_rebuild_hflags(s); - /* Many factors, including TCO, go into MTE_ACTIVE. */ - s->base.is_jmp = DISAS_UPDATE_NOCHAIN; - } else if (dc_isar_feature(aa64_mte_insn_reg, s)) { - /* Only "instructions accessible at EL0" -- PSTATE.TCO is WI. */ - s->base.is_jmp = DISAS_NEXT; - } else { - goto do_unallocated; - } - break; - - case 0x1b: /* SVCR* */ - if (!dc_isar_feature(aa64_sme, s) || crm < 2 || crm > 7) { - goto do_unallocated; - } - if (sme_access_check(s)) { - int old = s->pstate_sm | (s->pstate_za << 1); - int new = (crm & 1) * 3; - int msk = (crm >> 1) & 3; - - if ((old ^ new) & msk) { - /* At least one bit changes. */ - gen_helper_set_svcr(cpu_env, tcg_constant_i32(new), - tcg_constant_i32(msk)); - } else { - s->base.is_jmp = DISAS_NEXT; - } - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - return; + if (!dc_isar_feature(aa64_uao, s) || s->current_el == 0) { + return false; } + if (a->imm & 1) { + set_pstate_bits(PSTATE_UAO); + } else { + clear_pstate_bits(PSTATE_UAO); + } + gen_rebuild_hflags(s); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_PAN(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_pan, s) || s->current_el == 0) { + return false; + } + if (a->imm & 1) { + set_pstate_bits(PSTATE_PAN); + } else { + clear_pstate_bits(PSTATE_PAN); + } + gen_rebuild_hflags(s); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_SPSEL(DisasContext *s, arg_i *a) +{ + if (s->current_el == 0) { + return false; + } + gen_helper_msr_i_spsel(cpu_env, tcg_constant_i32(a->imm & PSTATE_SP)); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_SBSS(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_ssbs, s)) { + return false; + } + if (a->imm & 1) { + set_pstate_bits(PSTATE_SSBS); + } else { + clear_pstate_bits(PSTATE_SSBS); + } + /* Don't need to rebuild hflags since SSBS is a nop */ + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_DIT(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_dit, s)) { + return false; + } + if (a->imm & 1) { + set_pstate_bits(PSTATE_DIT); + } else { + clear_pstate_bits(PSTATE_DIT); + } + /* There's no need to rebuild hflags because DIT is a nop */ + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_TCO(DisasContext *s, arg_i *a) +{ + if (dc_isar_feature(aa64_mte, s)) { + /* Full MTE is enabled -- set the TCO bit as directed. */ + if (a->imm & 1) { + set_pstate_bits(PSTATE_TCO); + } else { + clear_pstate_bits(PSTATE_TCO); + } + gen_rebuild_hflags(s); + /* Many factors, including TCO, go into MTE_ACTIVE. */ + s->base.is_jmp = DISAS_UPDATE_NOCHAIN; + return true; + } else if (dc_isar_feature(aa64_mte_insn_reg, s)) { + /* Only "instructions accessible at EL0" -- PSTATE.TCO is WI. */ + return true; + } else { + /* Insn not present */ + return false; + } +} + +static bool trans_MSR_i_DAIFSET(DisasContext *s, arg_i *a) +{ + gen_helper_msr_i_daifset(cpu_env, tcg_constant_i32(a->imm)); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_DAIFCLEAR(DisasContext *s, arg_i *a) +{ + gen_helper_msr_i_daifclear(cpu_env, tcg_constant_i32(a->imm)); + /* Exit the cpu loop to re-evaluate pending IRQs. */ + s->base.is_jmp = DISAS_UPDATE_EXIT; + return true; +} + +static bool trans_MSR_i_SVCR(DisasContext *s, arg_MSR_i_SVCR *a) +{ + if (!dc_isar_feature(aa64_sme, s) || a->mask == 0) { + return false; + } + if (sme_access_check(s)) { + int old = s->pstate_sm | (s->pstate_za << 1); + int new = a->imm * 3; + + if ((old ^ new) & a->mask) { + /* At least one bit changes. */ + gen_helper_set_svcr(cpu_env, tcg_constant_i32(new), + tcg_constant_i32(a->mask)); + s->base.is_jmp = DISAS_TOO_MANY; + } + } + return true; } static void gen_get_nzcv(TCGv_i64 tcg_rt) @@ -2319,18 +2325,7 @@ static void disas_system(DisasContext *s, uint32_t insn) rt = extract32(insn, 0, 5); if (op0 == 0) { - if (l || rt != 31) { - unallocated_encoding(s); - return; - } - switch (crn) { - case 4: /* MSR (immediate) */ - handle_msr_i(s, insn, op1, op2, crm); - break; - default: - unallocated_encoding(s); - break; - } + unallocated_encoding(s); return; } handle_sys(s, insn, l, op0, op1, op2, crn, crm, rt); From 6e3c8049ad90951e81077d594f2cfe8c10047213 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:20 +0100 Subject: [PATCH 370/412] target/arm: Convert MSR (reg), MRS, SYS, SYSL to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert MSR (reg), MRS, SYS, SYSL to decodetree. For QEMU these are all essentially the same instruction (system register access). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-7-peter.maydell@linaro.org Reviewed-by: Philippe Mathieu-Daudé --- target/arm/tcg/a64.decode | 8 ++++++++ target/arm/tcg/translate-a64.c | 32 +++++--------------------------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 4f94a08907..c49215cca8 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -207,3 +207,11 @@ MSR_i_TCO 1101 0101 0000 0 011 0100 .... 100 11111 @msr_i MSR_i_DAIFSET 1101 0101 0000 0 011 0100 .... 110 11111 @msr_i MSR_i_DAIFCLEAR 1101 0101 0000 0 011 0100 .... 111 11111 @msr_i MSR_i_SVCR 1101 0101 0000 0 011 0100 0 mask:2 imm:1 011 11111 + +# MRS, MSR (register), SYS, SYSL. These are all essentially the +# same instruction as far as QEMU is concerned. +# NB: op0 is bits [20:19], but op0=0b00 is other insns, so we have +# to hand-decode it. +SYS 1101 0101 00 l:1 01 op1:3 crn:4 crm:4 op2:3 rt:5 op0=1 +SYS 1101 0101 00 l:1 10 op1:3 crn:4 crm:4 op2:3 rt:5 op0=2 +SYS 1101 0101 00 l:1 11 op1:3 crn:4 crm:4 op2:3 rt:5 op0=3 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8c57b48d81..74a389da4a 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2122,7 +2122,7 @@ static void gen_sysreg_undef(DisasContext *s, bool isread, * These are all essentially the same insn in 'read' and 'write' * versions, with varying op0 fields. */ -static void handle_sys(DisasContext *s, uint32_t insn, bool isread, +static void handle_sys(DisasContext *s, bool isread, unsigned int op0, unsigned int op1, unsigned int op2, unsigned int crn, unsigned int crm, unsigned int rt) { @@ -2307,28 +2307,10 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } } -/* System - * 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0 - * +---------------------+---+-----+-----+-------+-------+-----+------+ - * | 1 1 0 1 0 1 0 1 0 0 | L | op0 | op1 | CRn | CRm | op2 | Rt | - * +---------------------+---+-----+-----+-------+-------+-----+------+ - */ -static void disas_system(DisasContext *s, uint32_t insn) +static bool trans_SYS(DisasContext *s, arg_SYS *a) { - unsigned int l, op0, op1, crn, crm, op2, rt; - l = extract32(insn, 21, 1); - op0 = extract32(insn, 19, 2); - op1 = extract32(insn, 16, 3); - crn = extract32(insn, 12, 4); - crm = extract32(insn, 8, 4); - op2 = extract32(insn, 5, 3); - rt = extract32(insn, 0, 5); - - if (op0 == 0) { - unallocated_encoding(s); - return; - } - handle_sys(s, insn, l, op0, op1, op2, crn, crm, rt); + handle_sys(s, a->l, a->op0, a->op1, a->op2, a->crn, a->crm, a->rt); + return true; } /* Exception generation @@ -2435,11 +2417,7 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn) switch (extract32(insn, 25, 7)) { case 0x6a: /* Exception generation / System */ if (insn & (1 << 24)) { - if (extract32(insn, 22, 2) == 0) { - disas_system(s, insn); - } else { - unallocated_encoding(s); - } + unallocated_encoding(s); } else { disas_exc(s, insn); } From a97d3c18f61fd307bffd3579ba35fccd2d88aeb1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:21 +0100 Subject: [PATCH 371/412] target/arm: Convert exception generation instructions to decodetree Convert the exception generation instructions SVC, HVC, SMC, BRK and HLT to decodetree. The old decoder decoded the halting-debug insnns DCPS1, DCPS2 and DCPS3 just in order to then make them UNDEF; as with DRPS, we don't bother to decode them, but document the patterns in a64.decode. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-8-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 15 +++ target/arm/tcg/translate-a64.c | 173 ++++++++++++--------------------- 2 files changed, 79 insertions(+), 109 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c49215cca8..eeaca08ae8 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -215,3 +215,18 @@ MSR_i_SVCR 1101 0101 0000 0 011 0100 0 mask:2 imm:1 011 11111 SYS 1101 0101 00 l:1 01 op1:3 crn:4 crm:4 op2:3 rt:5 op0=1 SYS 1101 0101 00 l:1 10 op1:3 crn:4 crm:4 op2:3 rt:5 op0=2 SYS 1101 0101 00 l:1 11 op1:3 crn:4 crm:4 op2:3 rt:5 op0=3 + +# Exception generation + +@i16 .... .... ... imm:16 ... .. &i +SVC 1101 0100 000 ................ 000 01 @i16 +HVC 1101 0100 000 ................ 000 10 @i16 +SMC 1101 0100 000 ................ 000 11 @i16 +BRK 1101 0100 001 ................ 000 00 @i16 +HLT 1101 0100 010 ................ 000 00 @i16 +# These insns always UNDEF unless in halting debug state, which +# we don't implement. So we don't need to decode them. The patterns +# are listed here as documentation. +# DCPS1 1101 0100 101 ................ 000 01 @i16 +# DCPS2 1101 0100 101 ................ 000 10 @i16 +# DCPS3 1101 0100 101 ................ 000 11 @i16 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 74a389da4a..a2a71b4062 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2313,119 +2313,77 @@ static bool trans_SYS(DisasContext *s, arg_SYS *a) return true; } -/* Exception generation - * - * 31 24 23 21 20 5 4 2 1 0 - * +-----------------+-----+------------------------+-----+----+ - * | 1 1 0 1 0 1 0 0 | opc | imm16 | op2 | LL | - * +-----------------------+------------------------+----------+ - */ -static void disas_exc(DisasContext *s, uint32_t insn) +static bool trans_SVC(DisasContext *s, arg_i *a) { - int opc = extract32(insn, 21, 3); - int op2_ll = extract32(insn, 0, 5); - int imm16 = extract32(insn, 5, 16); - uint32_t syndrome; - - switch (opc) { - case 0: - /* For SVC, HVC and SMC we advance the single-step state - * machine before taking the exception. This is architecturally - * mandated, to ensure that single-stepping a system call - * instruction works properly. - */ - switch (op2_ll) { - case 1: /* SVC */ - syndrome = syn_aa64_svc(imm16); - if (s->fgt_svc) { - gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); - break; - } - gen_ss_advance(s); - gen_exception_insn(s, 4, EXCP_SWI, syndrome); - break; - case 2: /* HVC */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } - /* The pre HVC helper handles cases when HVC gets trapped - * as an undefined insn by runtime configuration. - */ - gen_a64_update_pc(s, 0); - gen_helper_pre_hvc(cpu_env); - gen_ss_advance(s); - gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(imm16), 2); - break; - case 3: /* SMC */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } - gen_a64_update_pc(s, 0); - gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa64_smc(imm16))); - gen_ss_advance(s); - gen_exception_insn_el(s, 4, EXCP_SMC, syn_aa64_smc(imm16), 3); - break; - default: - unallocated_encoding(s); - break; - } - break; - case 1: - if (op2_ll != 0) { - unallocated_encoding(s); - break; - } - /* BRK */ - gen_exception_bkpt_insn(s, syn_aa64_bkpt(imm16)); - break; - case 2: - if (op2_ll != 0) { - unallocated_encoding(s); - break; - } - /* HLT. This has two purposes. - * Architecturally, it is an external halting debug instruction. - * Since QEMU doesn't implement external debug, we treat this as - * it is required for halting debug disabled: it will UNDEF. - * Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction. - */ - if (semihosting_enabled(s->current_el == 0) && imm16 == 0xf000) { - gen_exception_internal_insn(s, EXCP_SEMIHOST); - } else { - unallocated_encoding(s); - } - break; - case 5: - if (op2_ll < 1 || op2_ll > 3) { - unallocated_encoding(s); - break; - } - /* DCPS1, DCPS2, DCPS3 */ - unallocated_encoding(s); - break; - default: - unallocated_encoding(s); - break; + /* + * For SVC, HVC and SMC we advance the single-step state + * machine before taking the exception. This is architecturally + * mandated, to ensure that single-stepping a system call + * instruction works properly. + */ + uint32_t syndrome = syn_aa64_svc(a->imm); + if (s->fgt_svc) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + return true; } + gen_ss_advance(s); + gen_exception_insn(s, 4, EXCP_SWI, syndrome); + return true; } -/* Branches, exception generating and system instructions */ -static void disas_b_exc_sys(DisasContext *s, uint32_t insn) +static bool trans_HVC(DisasContext *s, arg_i *a) { - switch (extract32(insn, 25, 7)) { - case 0x6a: /* Exception generation / System */ - if (insn & (1 << 24)) { - unallocated_encoding(s); - } else { - disas_exc(s, insn); - } - break; - default: + if (s->current_el == 0) { unallocated_encoding(s); - break; + return true; } + /* + * The pre HVC helper handles cases when HVC gets trapped + * as an undefined insn by runtime configuration. + */ + gen_a64_update_pc(s, 0); + gen_helper_pre_hvc(cpu_env); + /* Architecture requires ss advance before we do the actual work */ + gen_ss_advance(s); + gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(a->imm), 2); + return true; +} + +static bool trans_SMC(DisasContext *s, arg_i *a) +{ + if (s->current_el == 0) { + unallocated_encoding(s); + return true; + } + gen_a64_update_pc(s, 0); + gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa64_smc(a->imm))); + /* Architecture requires ss advance before we do the actual work */ + gen_ss_advance(s); + gen_exception_insn_el(s, 4, EXCP_SMC, syn_aa64_smc(a->imm), 3); + return true; +} + +static bool trans_BRK(DisasContext *s, arg_i *a) +{ + gen_exception_bkpt_insn(s, syn_aa64_bkpt(a->imm)); + return true; +} + +static bool trans_HLT(DisasContext *s, arg_i *a) +{ + /* + * HLT. This has two purposes. + * Architecturally, it is an external halting debug instruction. + * Since QEMU doesn't implement external debug, we treat this as + * it is required for halting debug disabled: it will UNDEF. + * Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction. + */ + if (semihosting_enabled(s->current_el == 0) && a->imm == 0xf000) { + gen_exception_internal_insn(s, EXCP_SEMIHOST); + } else { + unallocated_encoding(s); + } + return true; } /* @@ -14188,9 +14146,6 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) static void disas_a64_legacy(DisasContext *s, uint32_t insn) { switch (extract32(insn, 25, 4)) { - case 0xa: case 0xb: /* Branch, exception generation and system insns */ - disas_b_exc_sys(s, insn); - break; case 0x4: case 0x6: case 0xc: From 84693e67fa5a6ffa14133c3038c57988b71dd135 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:21 +0100 Subject: [PATCH 372/412] target/arm: Convert load/store exclusive and ordered to decodetree Convert the instructions in the load/store exclusive (STXR, STLXR, LDXR, LDAXR) and load/store ordered (STLR, STLLR, LDAR, LDLAR) to decodetree. Note that for STLR, STLLR, LDAR, LDLAR this fixes an under-decoding in the legacy decoder where we were not checking that the RES1 bits in the Rs and Rt2 fields were set. The new function ldst_iss_sf() is equivalent to the existing disas_ldst_compute_iss_sf(), but it takes the pre-decoded 'ext' field rather than taking an undecoded two-bit opc field and extracting 'ext' from it. Once all the loads and stores have been converted to decodetree disas_ldst_compute_iss_sf() will be unused and can be deleted. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-9-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 11 +++ target/arm/tcg/translate-a64.c | 154 ++++++++++++++++++++------------- 2 files changed, 103 insertions(+), 62 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index eeaca08ae8..c5894fc06d 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -230,3 +230,14 @@ HLT 1101 0100 010 ................ 000 00 @i16 # DCPS1 1101 0100 101 ................ 000 01 @i16 # DCPS2 1101 0100 101 ................ 000 10 @i16 # DCPS3 1101 0100 101 ................ 000 11 @i16 + +# Loads and stores + +&stxr rn rt rt2 rs sz lasr +&stlr rn rt sz lasr +@stxr sz:2 ...... ... rs:5 lasr:1 rt2:5 rn:5 rt:5 &stxr +@stlr sz:2 ...... ... ..... lasr:1 ..... rn:5 rt:5 &stlr +STXR .. 001000 000 ..... . ..... ..... ..... @stxr # inc STLXR +LDXR .. 001000 010 ..... . ..... ..... ..... @stxr # inc LDAXR +STLR .. 001000 100 11111 . 11111 ..... ..... @stlr # inc STLLR +LDAR .. 001000 110 11111 . 11111 ..... ..... @stlr # inc LDLAR diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a2a71b4062..1ba2d6a75e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2652,6 +2652,95 @@ static bool disas_ldst_compute_iss_sf(int size, bool is_signed, int opc) return regsize == 64; } +static bool ldst_iss_sf(int size, bool sign, bool ext) +{ + + if (sign) { + /* + * Signed loads are 64 bit results if we are not going to + * do a zero-extend from 32 to 64 after the load. + * (For a store, sign and ext are always false.) + */ + return !ext; + } else { + /* Unsigned loads/stores work at the specified size */ + return size == MO_64; + } +} + +static bool trans_STXR(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + gen_store_exclusive(s, a->rs, a->rt, a->rt2, a->rn, a->sz, false); + return true; +} + +static bool trans_LDXR(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + gen_load_exclusive(s, a->rt, a->rt2, a->rn, a->sz, false); + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return true; +} + +static bool trans_STLR(DisasContext *s, arg_stlr *a) +{ + TCGv_i64 clean_addr; + MemOp memop; + bool iss_sf = ldst_iss_sf(a->sz, false, false); + + /* + * StoreLORelease is the same as Store-Release for QEMU, but + * needs the feature-test. + */ + if (!a->lasr && !dc_isar_feature(aa64_lor, s)) { + return false; + } + /* Generate ISS for non-exclusive accesses including LASR. */ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + memop = check_ordered_align(s, a->rn, 0, true, a->sz); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), + true, a->rn != 31, memop); + do_gpr_st(s, cpu_reg(s, a->rt), clean_addr, memop, true, a->rt, + iss_sf, a->lasr); + return true; +} + +static bool trans_LDAR(DisasContext *s, arg_stlr *a) +{ + TCGv_i64 clean_addr; + MemOp memop; + bool iss_sf = ldst_iss_sf(a->sz, false, false); + + /* LoadLOAcquire is the same as Load-Acquire for QEMU. */ + if (!a->lasr && !dc_isar_feature(aa64_lor, s)) { + return false; + } + /* Generate ISS for non-exclusive accesses including LASR. */ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + memop = check_ordered_align(s, a->rn, 0, false, a->sz); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), + false, a->rn != 31, memop); + do_gpr_ld(s, cpu_reg(s, a->rt), clean_addr, memop, false, true, + a->rt, iss_sf, a->lasr); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return true; +} + /* Load/store exclusive * * 31 30 29 24 23 22 21 20 16 15 14 10 9 5 4 0 @@ -2674,70 +2763,8 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) int is_lasr = extract32(insn, 15, 1); int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; int size = extract32(insn, 30, 2); - TCGv_i64 clean_addr; - MemOp memop; switch (o2_L_o1_o0) { - case 0x0: /* STXR */ - case 0x1: /* STLXR */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - gen_store_exclusive(s, rs, rt, rt2, rn, size, false); - return; - - case 0x4: /* LDXR */ - case 0x5: /* LDAXR */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - gen_load_exclusive(s, rt, rt2, rn, size, false); - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - return; - - case 0x8: /* STLLR */ - if (!dc_isar_feature(aa64_lor, s)) { - break; - } - /* StoreLORelease is the same as Store-Release for QEMU. */ - /* fall through */ - case 0x9: /* STLR */ - /* Generate ISS for non-exclusive accesses including LASR. */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - memop = check_ordered_align(s, rn, 0, true, size); - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, memop); - do_gpr_st(s, cpu_reg(s, rt), clean_addr, memop, true, rt, - disas_ldst_compute_iss_sf(size, false, 0), is_lasr); - return; - - case 0xc: /* LDLAR */ - if (!dc_isar_feature(aa64_lor, s)) { - break; - } - /* LoadLOAcquire is the same as Load-Acquire for QEMU. */ - /* fall through */ - case 0xd: /* LDAR */ - /* Generate ISS for non-exclusive accesses including LASR. */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - memop = check_ordered_align(s, rn, 0, false, size); - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, memop); - do_gpr_ld(s, cpu_reg(s, rt), clean_addr, memop, false, true, - rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - return; - case 0x2: case 0x3: /* CASP / STXP */ if (size & 2) { /* STXP / STLXP */ if (rn == 31) { @@ -2787,6 +2814,9 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) return; } break; + default: + /* Handled in decodetree */ + break; } unallocated_encoding(s); } From e8a149a359f4d518bdd5dac320fc16ef0909dc77 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:21 +0100 Subject: [PATCH 373/412] target/arm: Convert LDXP, STXP, CASP, CAS to decodetree Convert the load/store exclusive pair (LDXP, STXP, LDAXP, STLXP), compare-and-swap pair (CASP, CASPA, CASPAL, CASPL), and compare-and swap (CAS, CASA, CASAL, CASL) instructions to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-10-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 11 +++ target/arm/tcg/translate-a64.c | 121 ++++++++++++--------------------- 2 files changed, 53 insertions(+), 79 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c5894fc06d..6b1079b8bd 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -237,7 +237,18 @@ HLT 1101 0100 010 ................ 000 00 @i16 &stlr rn rt sz lasr @stxr sz:2 ...... ... rs:5 lasr:1 rt2:5 rn:5 rt:5 &stxr @stlr sz:2 ...... ... ..... lasr:1 ..... rn:5 rt:5 &stlr +%imm1_30_p2 30:1 !function=plus_2 +@stxp .. ...... ... rs:5 lasr:1 rt2:5 rn:5 rt:5 &stxr sz=%imm1_30_p2 STXR .. 001000 000 ..... . ..... ..... ..... @stxr # inc STLXR LDXR .. 001000 010 ..... . ..... ..... ..... @stxr # inc LDAXR STLR .. 001000 100 11111 . 11111 ..... ..... @stlr # inc STLLR LDAR .. 001000 110 11111 . 11111 ..... ..... @stlr # inc LDLAR + +STXP 1 . 001000 001 ..... . ..... ..... ..... @stxp # inc STLXP +LDXP 1 . 001000 011 ..... . ..... ..... ..... @stxp # inc LDAXP + +# CASP, CASPA, CASPAL, CASPL (we don't decode the bits that determine +# acquire/release semantics because QEMU's cmpxchg always has those) +CASP 0 . 001000 0 - 1 rs:5 - 11111 rn:5 rt:5 sz=%imm1_30_p2 +# CAS, CASA, CASAL, CASL +CAS sz:2 001000 1 - 1 rs:5 - 11111 rn:5 rt:5 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1ba2d6a75e..ff4338ee4d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2741,84 +2741,50 @@ static bool trans_LDAR(DisasContext *s, arg_stlr *a) return true; } -/* Load/store exclusive - * - * 31 30 29 24 23 22 21 20 16 15 14 10 9 5 4 0 - * +-----+-------------+----+---+----+------+----+-------+------+------+ - * | sz | 0 0 1 0 0 0 | o2 | L | o1 | Rs | o0 | Rt2 | Rn | Rt | - * +-----+-------------+----+---+----+------+----+-------+------+------+ - * - * sz: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64 bit - * L: 0 -> store, 1 -> load - * o2: 0 -> exclusive, 1 -> not - * o1: 0 -> single register, 1 -> register pair - * o0: 1 -> load-acquire/store-release, 0 -> not - */ -static void disas_ldst_excl(DisasContext *s, uint32_t insn) +static bool trans_STXP(DisasContext *s, arg_stxr *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rt2 = extract32(insn, 10, 5); - int rs = extract32(insn, 16, 5); - int is_lasr = extract32(insn, 15, 1); - int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; - int size = extract32(insn, 30, 2); - - switch (o2_L_o1_o0) { - case 0x2: case 0x3: /* CASP / STXP */ - if (size & 2) { /* STXP / STLXP */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - gen_store_exclusive(s, rs, rt, rt2, rn, size, true); - return; - } - if (rt2 == 31 - && ((rt | rs) & 1) == 0 - && dc_isar_feature(aa64_atomics, s)) { - /* CASP / CASPL */ - gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); - return; - } - break; - - case 0x6: case 0x7: /* CASPA / LDXP */ - if (size & 2) { /* LDXP / LDAXP */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - gen_load_exclusive(s, rt, rt2, rn, size, true); - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - return; - } - if (rt2 == 31 - && ((rt | rs) & 1) == 0 - && dc_isar_feature(aa64_atomics, s)) { - /* CASPA / CASPAL */ - gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); - return; - } - break; - - case 0xa: /* CAS */ - case 0xb: /* CASL */ - case 0xe: /* CASA */ - case 0xf: /* CASAL */ - if (rt2 == 31 && dc_isar_feature(aa64_atomics, s)) { - gen_compare_and_swap(s, rs, rt, rn, size); - return; - } - break; - default: - /* Handled in decodetree */ - break; + if (a->rn == 31) { + gen_check_sp_alignment(s); } - unallocated_encoding(s); + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + gen_store_exclusive(s, a->rs, a->rt, a->rt2, a->rn, a->sz, true); + return true; +} + +static bool trans_LDXP(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + gen_load_exclusive(s, a->rt, a->rt2, a->rn, a->sz, true); + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return true; +} + +static bool trans_CASP(DisasContext *s, arg_CASP *a) +{ + if (!dc_isar_feature(aa64_atomics, s)) { + return false; + } + if (((a->rt | a->rs) & 1) != 0) { + return false; + } + + gen_compare_and_swap_pair(s, a->rs, a->rt, a->rn, a->sz); + return true; +} + +static bool trans_CAS(DisasContext *s, arg_CAS *a) +{ + if (!dc_isar_feature(aa64_atomics, s)) { + return false; + } + gen_compare_and_swap(s, a->rs, a->rt, a->rn, a->sz); + return true; } /* @@ -4247,9 +4213,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) static void disas_ldst(DisasContext *s, uint32_t insn) { switch (extract32(insn, 24, 6)) { - case 0x08: /* Load/store exclusive */ - disas_ldst_excl(s, insn); - break; case 0x18: case 0x1c: /* Load register (literal) */ disas_ld_lit(s, insn); break; From a752c2f45968951c99f35130e76b191dedebcee7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:21 +0100 Subject: [PATCH 374/412] target/arm: Convert load reg (literal) group to decodetree Convert the "Load register (literal)" instruction class to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-11-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 13 ++++++ target/arm/tcg/translate-a64.c | 76 ++++++++++------------------------ 2 files changed, 35 insertions(+), 54 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 6b1079b8bd..c2c6ac0196 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -252,3 +252,16 @@ LDXP 1 . 001000 011 ..... . ..... ..... ..... @stxp # inc LDAXP CASP 0 . 001000 0 - 1 rs:5 - 11111 rn:5 rt:5 sz=%imm1_30_p2 # CAS, CASA, CASAL, CASL CAS sz:2 001000 1 - 1 rs:5 - 11111 rn:5 rt:5 + +&ldlit rt imm sz sign +@ldlit .. ... . .. ................... rt:5 &ldlit imm=%imm19 + +LD_lit 00 011 0 00 ................... ..... @ldlit sz=2 sign=0 +LD_lit 01 011 0 00 ................... ..... @ldlit sz=3 sign=0 +LD_lit 10 011 0 00 ................... ..... @ldlit sz=2 sign=1 +LD_lit_v 00 011 1 00 ................... ..... @ldlit sz=2 sign=0 +LD_lit_v 01 011 1 00 ................... ..... @ldlit sz=3 sign=0 +LD_lit_v 10 011 1 00 ................... ..... @ldlit sz=4 sign=0 + +# PRFM +NOP 11 011 0 00 ------------------- ----- diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ff4338ee4d..d1df41f2e5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2787,62 +2787,33 @@ static bool trans_CAS(DisasContext *s, arg_CAS *a) return true; } -/* - * Load register (literal) - * - * 31 30 29 27 26 25 24 23 5 4 0 - * +-----+-------+---+-----+-------------------+-------+ - * | opc | 0 1 1 | V | 0 0 | imm19 | Rt | - * +-----+-------+---+-----+-------------------+-------+ - * - * V: 1 -> vector (simd/fp) - * opc (non-vector): 00 -> 32 bit, 01 -> 64 bit, - * 10-> 32 bit signed, 11 -> prefetch - * opc (vector): 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit (11 unallocated) - */ -static void disas_ld_lit(DisasContext *s, uint32_t insn) +static bool trans_LD_lit(DisasContext *s, arg_ldlit *a) { - int rt = extract32(insn, 0, 5); - int64_t imm = sextract32(insn, 5, 19) << 2; - bool is_vector = extract32(insn, 26, 1); - int opc = extract32(insn, 30, 2); - bool is_signed = false; - int size = 2; - TCGv_i64 tcg_rt, clean_addr; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, false); + TCGv_i64 tcg_rt = cpu_reg(s, a->rt); + TCGv_i64 clean_addr = tcg_temp_new_i64(); + MemOp memop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + + gen_pc_plus_diff(s, clean_addr, a->imm); + do_gpr_ld(s, tcg_rt, clean_addr, memop, + false, true, a->rt, iss_sf, false); + return true; +} + +static bool trans_LD_lit_v(DisasContext *s, arg_ldlit *a) +{ + /* Load register (literal), vector version */ + TCGv_i64 clean_addr; MemOp memop; - if (is_vector) { - if (opc == 3) { - unallocated_encoding(s); - return; - } - size = 2 + opc; - if (!fp_access_check(s)) { - return; - } - memop = finalize_memop_asimd(s, size); - } else { - if (opc == 3) { - /* PRFM (literal) : prefetch */ - return; - } - size = 2 + extract32(opc, 0, 1); - is_signed = extract32(opc, 1, 1); - memop = finalize_memop(s, size + is_signed * MO_SIGN); + if (!fp_access_check(s)) { + return true; } - - tcg_rt = cpu_reg(s, rt); - + memop = finalize_memop_asimd(s, a->sz); clean_addr = tcg_temp_new_i64(); - gen_pc_plus_diff(s, clean_addr, imm); - - if (is_vector) { - do_fp_ld(s, rt, clean_addr, memop); - } else { - /* Only unsigned 32bit loads target 32bit registers. */ - bool iss_sf = opc != 0; - do_gpr_ld(s, tcg_rt, clean_addr, memop, false, true, rt, iss_sf, false); - } + gen_pc_plus_diff(s, clean_addr, a->imm); + do_fp_ld(s, a->rt, clean_addr, memop); + return true; } /* @@ -4213,9 +4184,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) static void disas_ldst(DisasContext *s, uint32_t insn) { switch (extract32(insn, 24, 6)) { - case 0x18: case 0x1c: /* Load register (literal) */ - disas_ld_lit(s, insn); - break; case 0x28: case 0x29: case 0x2c: case 0x2d: /* Load/store pair (all forms) */ disas_ldst_pair(s, insn); From 8c212eb65944097d70a28195985acd0ba2a016bc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:22 +0100 Subject: [PATCH 375/412] target/arm: Convert load/store-pair to decodetree Convert the load/store register pair insns (LDP, STP, LDNP, STNP, LDPSW, STGP) to decodetree. Signed-off-by: Peter Maydell Message-id: 20230602155223.2040685-12-peter.maydell@linaro.org Reviewed-by: Richard Henderson --- target/arm/tcg/a64.decode | 61 +++++ target/arm/tcg/translate-a64.c | 422 ++++++++++++++++----------------- 2 files changed, 268 insertions(+), 215 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c2c6ac0196..f578791993 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -265,3 +265,64 @@ LD_lit_v 10 011 1 00 ................... ..... @ldlit sz=4 sign=0 # PRFM NOP 11 011 0 00 ------------------- ----- + +&ldstpair rt2 rt rn imm sz sign w p +@ldstpair .. ... . ... . imm:s7 rt2:5 rn:5 rt:5 &ldstpair + +# STNP, LDNP: Signed offset, non-temporal hint. We don't emulate caches +# so we ignore hints about data access patterns, and handle these like +# plain signed offset. +STP 00 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP 00 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +STP 10 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP 10 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 00 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP_v 00 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +STP_v 01 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP_v 01 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 10 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 +LDP_v 10 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 + +# STP and LDP: post-indexed +STP 00 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +LDP 00 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +LDP 01 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=1 w=1 +STP 10 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +LDP 10 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +STP_v 00 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +LDP_v 00 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +STP_v 01 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +LDP_v 01 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +STP_v 10 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1 +LDP_v 10 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1 + +# STP and LDP: offset +STP 00 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP 00 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP 01 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=0 +STP 10 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP 10 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 00 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP_v 00 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +STP_v 01 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP_v 01 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 10 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 +LDP_v 10 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 + +# STP and LDP: pre-indexed +STP 00 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +LDP 00 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +LDP 01 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=1 +STP 10 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +LDP 10 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +STP_v 00 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +LDP_v 00 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +STP_v 01 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +LDP_v 01 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +STP_v 10 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1 +LDP_v 10 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1 + +# STGP: store tag and pair +STGP 01 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +STGP 01 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STGP 01 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d1df41f2e5..103e54d0c4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2816,229 +2816,225 @@ static bool trans_LD_lit_v(DisasContext *s, arg_ldlit *a) return true; } -/* - * LDNP (Load Pair - non-temporal hint) - * LDP (Load Pair - non vector) - * LDPSW (Load Pair Signed Word - non vector) - * STNP (Store Pair - non-temporal hint) - * STP (Store Pair - non vector) - * LDNP (Load Pair of SIMD&FP - non-temporal hint) - * LDP (Load Pair of SIMD&FP) - * STNP (Store Pair of SIMD&FP - non-temporal hint) - * STP (Store Pair of SIMD&FP) - * - * 31 30 29 27 26 25 24 23 22 21 15 14 10 9 5 4 0 - * +-----+-------+---+---+-------+---+-----------------------------+ - * | opc | 1 0 1 | V | 0 | index | L | imm7 | Rt2 | Rn | Rt | - * +-----+-------+---+---+-------+---+-------+-------+------+------+ - * - * opc: LDP/STP/LDNP/STNP 00 -> 32 bit, 10 -> 64 bit - * LDPSW/STGP 01 - * LDP/STP/LDNP/STNP (SIMD) 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit - * V: 0 -> GPR, 1 -> Vector - * idx: 00 -> signed offset with non-temporal hint, 01 -> post-index, - * 10 -> signed offset, 11 -> pre-index - * L: 0 -> Store 1 -> Load - * - * Rt, Rt2 = GPR or SIMD registers to be stored - * Rn = general purpose register containing address - * imm7 = signed offset (multiple of 4 or 8 depending on size) - */ -static void disas_ldst_pair(DisasContext *s, uint32_t insn) +static void op_addr_ldstpair_pre(DisasContext *s, arg_ldstpair *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + uint64_t offset, bool is_store, MemOp mop) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rt2 = extract32(insn, 10, 5); - uint64_t offset = sextract64(insn, 15, 7); - int index = extract32(insn, 23, 2); - bool is_vector = extract32(insn, 26, 1); - bool is_load = extract32(insn, 22, 1); - int opc = extract32(insn, 30, 2); - bool is_signed = false; - bool postindex = false; - bool wback = false; - bool set_tag = false; - TCGv_i64 clean_addr, dirty_addr; - MemOp mop; - int size; - - if (opc == 3) { - unallocated_encoding(s); - return; - } - - if (is_vector) { - size = 2 + opc; - } else if (opc == 1 && !is_load) { - /* STGP */ - if (!dc_isar_feature(aa64_mte_insn_reg, s) || index == 0) { - unallocated_encoding(s); - return; - } - size = 3; - set_tag = true; - } else { - size = 2 + extract32(opc, 1, 1); - is_signed = extract32(opc, 0, 1); - if (!is_load && is_signed) { - unallocated_encoding(s); - return; - } - } - - switch (index) { - case 1: /* post-index */ - postindex = true; - wback = true; - break; - case 0: - /* signed offset with "non-temporal" hint. Since we don't emulate - * caches we don't care about hints to the cache system about - * data access patterns, and handle this identically to plain - * signed offset. - */ - if (is_signed) { - /* There is no non-temporal-hint version of LDPSW */ - unallocated_encoding(s); - return; - } - postindex = false; - break; - case 2: /* signed offset, rn not updated */ - postindex = false; - break; - case 3: /* pre-index */ - postindex = false; - wback = true; - break; - } - - if (is_vector && !fp_access_check(s)) { - return; - } - - offset <<= (set_tag ? LOG2_TAG_GRANULE : size); - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - if (!postindex) { + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + if (!a->p) { + tcg_gen_addi_i64(*dirty_addr, *dirty_addr, offset); + } + + *clean_addr = gen_mte_checkN(s, *dirty_addr, is_store, + (a->w || a->rn != 31), 2 << a->sz, mop); +} + +static void op_addr_ldstpair_post(DisasContext *s, arg_ldstpair *a, + TCGv_i64 dirty_addr, uint64_t offset) +{ + if (a->w) { + if (a->p) { + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + } + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); + } +} + +static bool trans_STP(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr, tcg_rt, tcg_rt2; + MemOp mop = finalize_memop(s, a->sz); + + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, true, mop); + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); + /* + * We built mop above for the single logical access -- rebuild it + * now for the paired operation. + * + * With LSE2, non-sign-extending pairs are treated atomically if + * aligned, and if unaligned one of the pair will be completely + * within a 16-byte block and that element will be atomic. + * Otherwise each element is separately atomic. + * In all cases, issue one operation with the correct atomicity. + */ + mop = a->sz + 1; + if (s->align_mem) { + mop |= (a->sz == 2 ? MO_ALIGN_4 : MO_ALIGN_8); + } + mop = finalize_memop_pair(s, mop); + if (a->sz == 2) { + TCGv_i64 tmp = tcg_temp_new_i64(); + + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat32_i64(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i64(tmp, clean_addr, get_mem_index(s), mop); + } else { + TCGv_i128 tmp = tcg_temp_new_i128(); + + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat_i64_i128(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); + } + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_LDP(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr, tcg_rt, tcg_rt2; + MemOp mop = finalize_memop(s, a->sz); + + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, false, mop); + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); + + /* + * We built mop above for the single logical access -- rebuild it + * now for the paired operation. + * + * With LSE2, non-sign-extending pairs are treated atomically if + * aligned, and if unaligned one of the pair will be completely + * within a 16-byte block and that element will be atomic. + * Otherwise each element is separately atomic. + * In all cases, issue one operation with the correct atomicity. + * + * This treats sign-extending loads like zero-extending loads, + * since that reuses the most code below. + */ + mop = a->sz + 1; + if (s->align_mem) { + mop |= (a->sz == 2 ? MO_ALIGN_4 : MO_ALIGN_8); + } + mop = finalize_memop_pair(s, mop); + if (a->sz == 2) { + int o2 = s->be_data == MO_LE ? 32 : 0; + int o1 = o2 ^ 32; + + tcg_gen_qemu_ld_i64(tcg_rt, clean_addr, get_mem_index(s), mop); + if (a->sign) { + tcg_gen_sextract_i64(tcg_rt2, tcg_rt, o2, 32); + tcg_gen_sextract_i64(tcg_rt, tcg_rt, o1, 32); + } else { + tcg_gen_extract_i64(tcg_rt2, tcg_rt, o2, 32); + tcg_gen_extract_i64(tcg_rt, tcg_rt, o1, 32); + } + } else { + TCGv_i128 tmp = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(tmp, clean_addr, get_mem_index(s), mop); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(tcg_rt, tcg_rt2, tmp); + } else { + tcg_gen_extr_i128_i64(tcg_rt2, tcg_rt, tmp); + } + } + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_STP_v(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + if (!fp_access_check(s)) { + return true; + } + + /* LSE2 does not merge FP pairs; leave these as separate operations. */ + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, true, mop); + do_fp_st(s, a->rt, clean_addr, mop); + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << a->sz); + do_fp_st(s, a->rt2, clean_addr, mop); + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_LDP_v(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + if (!fp_access_check(s)) { + return true; + } + + /* LSE2 does not merge FP pairs; leave these as separate operations. */ + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, false, mop); + do_fp_ld(s, a->rt, clean_addr, mop); + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << a->sz); + do_fp_ld(s, a->rt2, clean_addr, mop); + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_STGP(DisasContext *s, arg_ldstpair *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt, tcg_rt2; + uint64_t offset = a->imm << LOG2_TAG_GRANULE; + MemOp mop; + TCGv_i128 tmp; + + if (!dc_isar_feature(aa64_mte_insn_reg, s)) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + if (!a->p) { tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); } - if (set_tag) { - if (!s->ata) { - /* - * TODO: We could rely on the stores below, at least for - * system mode, if we arrange to add MO_ALIGN_16. - */ - gen_helper_stg_stub(cpu_env, dirty_addr); - } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_stg_parallel(cpu_env, dirty_addr, dirty_addr); - } else { - gen_helper_stg(cpu_env, dirty_addr, dirty_addr); - } - } - - if (is_vector) { - mop = finalize_memop_asimd(s, size); - } else { - mop = finalize_memop(s, size); - } - clean_addr = gen_mte_checkN(s, dirty_addr, !is_load, - (wback || rn != 31) && !set_tag, - 2 << size, mop); - - if (is_vector) { - /* LSE2 does not merge FP pairs; leave these as separate operations. */ - if (is_load) { - do_fp_ld(s, rt, clean_addr, mop); - } else { - do_fp_st(s, rt, clean_addr, mop); - } - tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); - if (is_load) { - do_fp_ld(s, rt2, clean_addr, mop); - } else { - do_fp_st(s, rt2, clean_addr, mop); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - TCGv_i64 tcg_rt2 = cpu_reg(s, rt2); - + if (!s->ata) { /* - * We built mop above for the single logical access -- rebuild it - * now for the paired operation. - * - * With LSE2, non-sign-extending pairs are treated atomically if - * aligned, and if unaligned one of the pair will be completely - * within a 16-byte block and that element will be atomic. - * Otherwise each element is separately atomic. - * In all cases, issue one operation with the correct atomicity. - * - * This treats sign-extending loads like zero-extending loads, - * since that reuses the most code below. + * TODO: We could rely on the stores below, at least for + * system mode, if we arrange to add MO_ALIGN_16. */ - mop = size + 1; - if (s->align_mem) { - mop |= (size == 2 ? MO_ALIGN_4 : MO_ALIGN_8); - } - mop = finalize_memop_pair(s, mop); - - if (is_load) { - if (size == 2) { - int o2 = s->be_data == MO_LE ? 32 : 0; - int o1 = o2 ^ 32; - - tcg_gen_qemu_ld_i64(tcg_rt, clean_addr, get_mem_index(s), mop); - if (is_signed) { - tcg_gen_sextract_i64(tcg_rt2, tcg_rt, o2, 32); - tcg_gen_sextract_i64(tcg_rt, tcg_rt, o1, 32); - } else { - tcg_gen_extract_i64(tcg_rt2, tcg_rt, o2, 32); - tcg_gen_extract_i64(tcg_rt, tcg_rt, o1, 32); - } - } else { - TCGv_i128 tmp = tcg_temp_new_i128(); - - tcg_gen_qemu_ld_i128(tmp, clean_addr, get_mem_index(s), mop); - if (s->be_data == MO_LE) { - tcg_gen_extr_i128_i64(tcg_rt, tcg_rt2, tmp); - } else { - tcg_gen_extr_i128_i64(tcg_rt2, tcg_rt, tmp); - } - } - } else { - if (size == 2) { - TCGv_i64 tmp = tcg_temp_new_i64(); - - if (s->be_data == MO_LE) { - tcg_gen_concat32_i64(tmp, tcg_rt, tcg_rt2); - } else { - tcg_gen_concat32_i64(tmp, tcg_rt2, tcg_rt); - } - tcg_gen_qemu_st_i64(tmp, clean_addr, get_mem_index(s), mop); - } else { - TCGv_i128 tmp = tcg_temp_new_i128(); - - if (s->be_data == MO_LE) { - tcg_gen_concat_i64_i128(tmp, tcg_rt, tcg_rt2); - } else { - tcg_gen_concat_i64_i128(tmp, tcg_rt2, tcg_rt); - } - tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); - } - } + gen_helper_stg_stub(cpu_env, dirty_addr); + } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { + gen_helper_stg_parallel(cpu_env, dirty_addr, dirty_addr); + } else { + gen_helper_stg(cpu_env, dirty_addr, dirty_addr); } - if (wback) { - if (postindex) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); + mop = finalize_memop(s, a->sz); + clean_addr = gen_mte_checkN(s, dirty_addr, true, false, 2 << a->sz, mop); + + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); + + assert(a->sz == 3); + + tmp = tcg_temp_new_i128(); + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat_i64_i128(tmp, tcg_rt2, tcg_rt); } + tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); + + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; } /* @@ -4184,10 +4180,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) static void disas_ldst(DisasContext *s, uint32_t insn) { switch (extract32(insn, 24, 6)) { - case 0x28: case 0x29: - case 0x2c: case 0x2d: /* Load/store pair (all forms) */ - disas_ldst_pair(s, insn); - break; case 0x38: case 0x39: case 0x3c: case 0x3d: /* Load/store register (all forms) */ disas_ldst_reg(s, insn); From 60cd7ba9c5ed0f19119ed5c385235a4500c120b6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:22 +0100 Subject: [PATCH 376/412] target/arm: Convert ld/st reg+imm9 insns to decodetree Convert the load and store instructions which use a 9-bit immediate offset to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-13-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 69 +++++++++++ target/arm/tcg/translate-a64.c | 206 ++++++++++++++------------------- 2 files changed, 153 insertions(+), 122 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f578791993..d55c09684a 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -326,3 +326,72 @@ LDP_v 10 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p STGP 01 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 STGP 01 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 STGP 01 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 + +# Load/store register (unscaled immediate) +&ldst_imm rt rn imm sz sign w p unpriv ext +@ldst_imm .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=0 p=0 w=0 +@ldst_imm_pre .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=0 p=0 w=1 +@ldst_imm_post .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=0 p=1 w=1 +@ldst_imm_user .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=1 p=0 w=0 + +STR_i sz:2 111 0 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=1 + +STR_i sz:2 111 0 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=1 + +STR_i sz:2 111 0 00 00 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=1 + +STR_i sz:2 111 0 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=1 + +# PRFM : prefetch memory: a no-op for QEMU +NOP 11 111 0 00 10 0 --------- 00 ----- ----- + +STR_v_i sz:2 111 1 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 +STR_v_i 00 111 1 00 10 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 +LDR_v_i 00 111 1 00 11 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4 + +STR_v_i sz:2 111 1 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 +STR_v_i 00 111 1 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 +LDR_v_i 00 111 1 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4 + +STR_v_i sz:2 111 1 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 +STR_v_i 00 111 1 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 +LDR_v_i 00 111 1 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 103e54d0c4..a1ddb1a9cd 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3037,134 +3037,101 @@ static bool trans_STGP(DisasContext *s, arg_ldstpair *a) return true; } -/* - * Load/store (immediate post-indexed) - * Load/store (immediate pre-indexed) - * Load/store (unscaled immediate) - * - * 31 30 29 27 26 25 24 23 22 21 20 12 11 10 9 5 4 0 - * +----+-------+---+-----+-----+---+--------+-----+------+------+ - * |size| 1 1 1 | V | 0 0 | opc | 0 | imm9 | idx | Rn | Rt | - * +----+-------+---+-----+-----+---+--------+-----+------+------+ - * - * idx = 01 -> post-indexed, 11 pre-indexed, 00 unscaled imm. (no writeback) - 10 -> unprivileged - * V = 0 -> non-vector - * size: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64bit - * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 - */ -static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, - int opc, - int size, - int rt, - bool is_vector) +static void op_addr_ldst_imm_pre(DisasContext *s, arg_ldst_imm *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + uint64_t offset, bool is_store, MemOp mop) { - int rn = extract32(insn, 5, 5); - int imm9 = sextract32(insn, 12, 9); - int idx = extract32(insn, 10, 2); - bool is_signed = false; - bool is_store = false; - bool is_extended = false; - bool is_unpriv = (idx == 2); - bool iss_valid; - bool post_index; - bool writeback; int memidx; - MemOp memop; - TCGv_i64 clean_addr, dirty_addr; - if (is_vector) { - size |= (opc & 2) << 1; - if (size > 4 || is_unpriv) { - unallocated_encoding(s); - return; - } - is_store = ((opc & 1) == 0); - if (!fp_access_check(s)) { - return; - } - memop = finalize_memop_asimd(s, size); - } else { - if (size == 3 && opc == 2) { - /* PRFM - prefetch */ - if (idx != 0) { - unallocated_encoding(s); - return; - } - return; - } - if (opc == 3 && size > 1) { - unallocated_encoding(s); - return; - } - is_store = (opc == 0); - is_signed = !is_store && extract32(opc, 1, 1); - is_extended = (size < 3) && extract32(opc, 0, 1); - memop = finalize_memop(s, size + is_signed * MO_SIGN); - } - - switch (idx) { - case 0: - case 2: - post_index = false; - writeback = false; - break; - case 1: - post_index = true; - writeback = true; - break; - case 3: - post_index = false; - writeback = true; - break; - default: - g_assert_not_reached(); - } - - iss_valid = !is_vector && !writeback; - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - if (!post_index) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, imm9); + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + if (!a->p) { + tcg_gen_addi_i64(*dirty_addr, *dirty_addr, offset); } + memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + *clean_addr = gen_mte_check1_mmuidx(s, *dirty_addr, is_store, + a->w || a->rn != 31, + mop, a->unpriv, memidx); +} - memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); - - clean_addr = gen_mte_check1_mmuidx(s, dirty_addr, is_store, - writeback || rn != 31, - memop, is_unpriv, memidx); - - if (is_vector) { - if (is_store) { - do_fp_st(s, rt, clean_addr, memop); - } else { - do_fp_ld(s, rt, clean_addr, memop); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); - - if (is_store) { - do_gpr_st_memidx(s, tcg_rt, clean_addr, memop, memidx, - iss_valid, rt, iss_sf, false); - } else { - do_gpr_ld_memidx(s, tcg_rt, clean_addr, memop, - is_extended, memidx, - iss_valid, rt, iss_sf, false); +static void op_addr_ldst_imm_post(DisasContext *s, arg_ldst_imm *a, + TCGv_i64 dirty_addr, uint64_t offset) +{ + if (a->w) { + if (a->p) { + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); } + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); } +} - if (writeback) { - TCGv_i64 tcg_rn = cpu_reg_sp(s, rn); - if (post_index) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, imm9); - } - tcg_gen_mov_i64(tcg_rn, dirty_addr); +static bool trans_STR_i(DisasContext *s, arg_ldst_imm *a) +{ + bool iss_sf, iss_valid = !a->w; + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + int memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + MemOp mop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, true, mop); + + tcg_rt = cpu_reg(s, a->rt); + iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + + do_gpr_st_memidx(s, tcg_rt, clean_addr, mop, memidx, + iss_valid, a->rt, iss_sf, false); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; +} + +static bool trans_LDR_i(DisasContext *s, arg_ldst_imm *a) +{ + bool iss_sf, iss_valid = !a->w; + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + int memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + MemOp mop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, false, mop); + + tcg_rt = cpu_reg(s, a->rt); + iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + + do_gpr_ld_memidx(s, tcg_rt, clean_addr, mop, + a->ext, memidx, iss_valid, a->rt, iss_sf, false); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; +} + +static bool trans_STR_v_i(DisasContext *s, arg_ldst_imm *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + if (!fp_access_check(s)) { + return true; } + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, true, mop); + do_fp_st(s, a->rt, clean_addr, mop); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; +} + +static bool trans_LDR_v_i(DisasContext *s, arg_ldst_imm *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + if (!fp_access_check(s)) { + return true; + } + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, false, mop); + do_fp_ld(s, a->rt, clean_addr, mop); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; } /* @@ -3637,12 +3604,7 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) switch (extract32(insn, 24, 2)) { case 0: if (extract32(insn, 21, 1) == 0) { - /* Load/store register (unscaled immediate) - * Load/store immediate pre/post-indexed - * Load/store register unprivileged - */ - disas_ldst_reg_imm9(s, insn, opc, size, rt, is_vector); - return; + break; } switch (extract32(insn, 10, 2)) { case 0: From 61edd8f878ec3eb51ff3cc5eb5a5e96cb64f534b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:22 +0100 Subject: [PATCH 377/412] target/arm: Convert LDR/STR with 12-bit immediate to decodetree Convert the LDR and STR instructions which use a 12-bit immediate offset to decodetree. We can reuse the existing LDR and STR trans functions for these. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-14-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 25 ++++++++ target/arm/tcg/translate-a64.c | 104 +++++---------------------------- 2 files changed, 41 insertions(+), 88 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d55c09684a..d6b31c1083 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -395,3 +395,28 @@ STR_v_i sz:2 111 1 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 STR_v_i 00 111 1 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4 LDR_v_i sz:2 111 1 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 LDR_v_i 00 111 1 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4 + +# Load/store with an unsigned 12 bit immediate, which is scaled by the +# element size. The function gets the sz:imm and returns the scaled immediate. +%uimm_scaled 10:12 sz:3 !function=uimm_scaled + +@ldst_uimm .. ... . .. .. ............ rn:5 rt:5 &ldst_imm unpriv=0 p=0 w=0 imm=%uimm_scaled + +STR_i sz:2 111 0 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0 +LDR_i 00 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=0 +LDR_i 01 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=1 +LDR_i 10 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=2 +LDR_i 11 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=3 +LDR_i 00 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=0 +LDR_i 01 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=1 +LDR_i 10 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=2 +LDR_i 00 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=0 +LDR_i 01 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=1 + +# PRFM +NOP 11 111 0 01 10 ------------ ----- ----- + +STR_v_i sz:2 111 1 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0 +STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 +LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a1ddb1a9cd..82da83d973 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -46,6 +46,22 @@ enum a64_shift_type { A64_SHIFT_TYPE_ROR = 3 }; +/* + * Helpers for extracting complex instruction fields + */ + +/* + * For load/store with an unsigned 12 bit immediate scaled by the element + * size. The input has the immediate field in bits [14:3] and the element + * size in [2:0]. + */ +static int uimm_scaled(DisasContext *s, int x) +{ + unsigned imm = x >> 3; + unsigned scale = extract32(x, 0, 3); + return imm << scale; +} + /* * Include the generated decoders. */ @@ -3234,91 +3250,6 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, } } -/* - * Load/store (unsigned immediate) - * - * 31 30 29 27 26 25 24 23 22 21 10 9 5 - * +----+-------+---+-----+-----+------------+-------+------+ - * |size| 1 1 1 | V | 0 1 | opc | imm12 | Rn | Rt | - * +----+-------+---+-----+-----+------------+-------+------+ - * - * For non-vector: - * size: 00-> byte, 01 -> 16 bit, 10 -> 32bit, 11 -> 64bit - * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 - * For vector: - * size is opc<1>:size<1:0> so 100 -> 128 bit; 110 and 111 unallocated - * opc<0>: 0 -> store, 1 -> load - * Rn: base address register (inc SP) - * Rt: target register - */ -static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, - int opc, - int size, - int rt, - bool is_vector) -{ - int rn = extract32(insn, 5, 5); - unsigned int imm12 = extract32(insn, 10, 12); - unsigned int offset; - TCGv_i64 clean_addr, dirty_addr; - bool is_store; - bool is_signed = false; - bool is_extended = false; - MemOp memop; - - if (is_vector) { - size |= (opc & 2) << 1; - if (size > 4) { - unallocated_encoding(s); - return; - } - is_store = !extract32(opc, 0, 1); - if (!fp_access_check(s)) { - return; - } - memop = finalize_memop_asimd(s, size); - } else { - if (size == 3 && opc == 2) { - /* PRFM - prefetch */ - return; - } - if (opc == 3 && size > 1) { - unallocated_encoding(s); - return; - } - is_store = (opc == 0); - is_signed = !is_store && extract32(opc, 1, 1); - is_extended = (size < 3) && extract32(opc, 0, 1); - memop = finalize_memop(s, size + is_signed * MO_SIGN); - } - - if (rn == 31) { - gen_check_sp_alignment(s); - } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - offset = imm12 << size; - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - - clean_addr = gen_mte_check1(s, dirty_addr, is_store, rn != 31, memop); - - if (is_vector) { - if (is_store) { - do_fp_st(s, rt, clean_addr, memop); - } else { - do_fp_ld(s, rt, clean_addr, memop); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); - if (is_store) { - do_gpr_st(s, tcg_rt, clean_addr, memop, true, rt, iss_sf, false); - } else { - do_gpr_ld(s, tcg_rt, clean_addr, memop, - is_extended, true, rt, iss_sf, false); - } - } -} - /* Atomic memory operations * * 31 30 27 26 24 22 21 16 15 12 10 5 0 @@ -3618,9 +3549,6 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) return; } break; - case 1: - disas_ldst_reg_unsigned_imm(s, insn, opc, size, rt, is_vector); - return; } unallocated_encoding(s); } From f36bf0c14a223b38a08fbb7e53e1a6a700735de9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:23 +0100 Subject: [PATCH 378/412] target/arm: Convert LDR/STR reg+reg to decodetree Convert the LDR and STR instructions which take a register plus register offset to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-15-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 22 +++++ target/arm/tcg/translate-a64.c | 173 +++++++++++++++------------------ 2 files changed, 103 insertions(+), 92 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d6b31c1083..5c086d6af6 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -420,3 +420,25 @@ STR_v_i sz:2 111 1 01 00 ............ ..... ..... @ldst_uimm sign=0 ext= STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4 LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4 + +# Load/store with register offset +&ldst rm rn rt sign ext sz opt s +@ldst .. ... . .. .. . rm:5 opt:3 s:1 .. rn:5 rt:5 &ldst +STR sz:2 111 0 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 +LDR 00 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=0 +LDR 01 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=1 +LDR 10 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=2 +LDR 11 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=3 +LDR 00 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=0 +LDR 01 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=1 +LDR 10 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=2 +LDR 00 111 0 00 11 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=1 sz=0 +LDR 01 111 0 00 11 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=1 sz=1 + +# PRFM +NOP 11 111 0 00 10 1 ----- -1- - 10 ----- ----- + +STR_v sz:2 111 1 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 +STR_v 00 111 1 00 10 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4 +LDR_v sz:2 111 1 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 +LDR_v 00 111 1 00 11 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 82da83d973..2d5e920c7b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3150,104 +3150,95 @@ static bool trans_LDR_v_i(DisasContext *s, arg_ldst_imm *a) return true; } -/* - * Load/store (register offset) - * - * 31 30 29 27 26 25 24 23 22 21 20 16 15 13 12 11 10 9 5 4 0 - * +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+ - * |size| 1 1 1 | V | 0 0 | opc | 1 | Rm | opt | S| 1 0 | Rn | Rt | - * +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+ - * - * For non-vector: - * size: 00-> byte, 01 -> 16 bit, 10 -> 32bit, 11 -> 64bit - * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 - * For vector: - * size is opc<1>:size<1:0> so 100 -> 128 bit; 110 and 111 unallocated - * opc<0>: 0 -> store, 1 -> load - * V: 1 -> vector/simd - * opt: extend encoding (see DecodeRegExtend) - * S: if S=1 then scale (essentially index by sizeof(size)) - * Rt: register to transfer into/out of - * Rn: address register or SP for base - * Rm: offset register or ZR for offset - */ -static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, - int opc, - int size, - int rt, - bool is_vector) +static void op_addr_ldst_pre(DisasContext *s, arg_ldst *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + bool is_store, MemOp memop) { - int rn = extract32(insn, 5, 5); - int shift = extract32(insn, 12, 1); - int rm = extract32(insn, 16, 5); - int opt = extract32(insn, 13, 3); - bool is_signed = false; - bool is_store = false; - bool is_extended = false; - TCGv_i64 tcg_rm, clean_addr, dirty_addr; - MemOp memop; + TCGv_i64 tcg_rm; - if (extract32(opt, 1, 1) == 0) { - unallocated_encoding(s); - return; - } - - if (is_vector) { - size |= (opc & 2) << 1; - if (size > 4) { - unallocated_encoding(s); - return; - } - is_store = !extract32(opc, 0, 1); - if (!fp_access_check(s)) { - return; - } - memop = finalize_memop_asimd(s, size); - } else { - if (size == 3 && opc == 2) { - /* PRFM - prefetch */ - return; - } - if (opc == 3 && size > 1) { - unallocated_encoding(s); - return; - } - is_store = (opc == 0); - is_signed = !is_store && extract32(opc, 1, 1); - is_extended = (size < 3) && extract32(opc, 0, 1); - memop = finalize_memop(s, size + is_signed * MO_SIGN); - } - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - dirty_addr = read_cpu_reg_sp(s, rn, 1); + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); - tcg_rm = read_cpu_reg(s, rm, 1); - ext_and_shift_reg(tcg_rm, tcg_rm, opt, shift ? size : 0); + tcg_rm = read_cpu_reg(s, a->rm, 1); + ext_and_shift_reg(tcg_rm, tcg_rm, a->opt, a->s ? a->sz : 0); - tcg_gen_add_i64(dirty_addr, dirty_addr, tcg_rm); + tcg_gen_add_i64(*dirty_addr, *dirty_addr, tcg_rm); + *clean_addr = gen_mte_check1(s, *dirty_addr, is_store, true, memop); +} - clean_addr = gen_mte_check1(s, dirty_addr, is_store, true, memop); +static bool trans_LDR(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + MemOp memop; - if (is_vector) { - if (is_store) { - do_fp_st(s, rt, clean_addr, memop); - } else { - do_fp_ld(s, rt, clean_addr, memop); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); - - if (is_store) { - do_gpr_st(s, tcg_rt, clean_addr, memop, - true, rt, iss_sf, false); - } else { - do_gpr_ld(s, tcg_rt, clean_addr, memop, - is_extended, true, rt, iss_sf, false); - } + if (extract32(a->opt, 1, 1) == 0) { + return false; } + + memop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, false, memop); + tcg_rt = cpu_reg(s, a->rt); + do_gpr_ld(s, tcg_rt, clean_addr, memop, + a->ext, true, a->rt, iss_sf, false); + return true; +} + +static bool trans_STR(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; + } + + memop = finalize_memop(s, a->sz); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, true, memop); + tcg_rt = cpu_reg(s, a->rt); + do_gpr_st(s, tcg_rt, clean_addr, memop, true, a->rt, iss_sf, false); + return true; +} + +static bool trans_LDR_v(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; + } + + if (!fp_access_check(s)) { + return true; + } + + memop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, false, memop); + do_fp_ld(s, a->rt, clean_addr, memop); + return true; +} + +static bool trans_STR_v(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; + } + + if (!fp_access_check(s)) { + return true; + } + + memop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, true, memop); + do_fp_st(s, a->rt, clean_addr, memop); + return true; } /* Atomic memory operations @@ -3528,7 +3519,6 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) static void disas_ldst_reg(DisasContext *s, uint32_t insn) { int rt = extract32(insn, 0, 5); - int opc = extract32(insn, 22, 2); bool is_vector = extract32(insn, 26, 1); int size = extract32(insn, 30, 2); @@ -3542,8 +3532,7 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) disas_ldst_atomic(s, insn, size, rt, is_vector); return; case 2: - disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); - return; + break; default: disas_ldst_pac(s, insn, size, rt, is_vector); return; From 54a9ab74edd34c516552113a89bfc5eb1ec61c8e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:23 +0100 Subject: [PATCH 379/412] target/arm: Convert atomic memory ops to decodetree Convert the insns in the atomic memory operations group to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-16-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 15 ++++ target/arm/tcg/translate-a64.c | 153 ++++++++++++--------------------- 2 files changed, 70 insertions(+), 98 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5c086d6af6..799c5ecb77 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -442,3 +442,18 @@ STR_v sz:2 111 1 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 STR_v 00 111 1 00 10 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4 LDR_v sz:2 111 1 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 LDR_v 00 111 1 00 11 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4 + +# Atomic memory operations +&atomic rs rn rt a r sz +@atomic sz:2 ... . .. a:1 r:1 . rs:5 . ... .. rn:5 rt:5 &atomic +LDADD .. 111 0 00 . . 1 ..... 0000 00 ..... ..... @atomic +LDCLR .. 111 0 00 . . 1 ..... 0001 00 ..... ..... @atomic +LDEOR .. 111 0 00 . . 1 ..... 0010 00 ..... ..... @atomic +LDSET .. 111 0 00 . . 1 ..... 0011 00 ..... ..... @atomic +LDSMAX .. 111 0 00 . . 1 ..... 0100 00 ..... ..... @atomic +LDSMIN .. 111 0 00 . . 1 ..... 0101 00 ..... ..... @atomic +LDUMAX .. 111 0 00 . . 1 ..... 0110 00 ..... ..... @atomic +LDUMIN .. 111 0 00 . . 1 ..... 0111 00 ..... ..... @atomic +SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic + +LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2d5e920c7b..6dc8151c40 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3241,113 +3241,32 @@ static bool trans_STR_v(DisasContext *s, arg_ldst *a) return true; } -/* Atomic memory operations - * - * 31 30 27 26 24 22 21 16 15 12 10 5 0 - * +------+-------+---+-----+-----+---+----+----+-----+-----+----+-----+ - * | size | 1 1 1 | V | 0 0 | A R | 1 | Rs | o3 | opc | 0 0 | Rn | Rt | - * +------+-------+---+-----+-----+--------+----+-----+-----+----+-----+ - * - * Rt: the result register - * Rn: base address or SP - * Rs: the source register for the operation - * V: vector flag (always 0 as of v8.3) - * A: acquire flag - * R: release flag - */ -static void disas_ldst_atomic(DisasContext *s, uint32_t insn, - int size, int rt, bool is_vector) + +static bool do_atomic_ld(DisasContext *s, arg_atomic *a, AtomicThreeOpFn *fn, + int sign, bool invert) { - int rs = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int o3_opc = extract32(insn, 12, 4); - bool r = extract32(insn, 22, 1); - bool a = extract32(insn, 23, 1); - TCGv_i64 tcg_rs, tcg_rt, clean_addr; - AtomicThreeOpFn *fn = NULL; - MemOp mop = size; + MemOp mop = a->sz | sign; + TCGv_i64 clean_addr, tcg_rs, tcg_rt; - if (is_vector || !dc_isar_feature(aa64_atomics, s)) { - unallocated_encoding(s); - return; - } - switch (o3_opc) { - case 000: /* LDADD */ - fn = tcg_gen_atomic_fetch_add_i64; - break; - case 001: /* LDCLR */ - fn = tcg_gen_atomic_fetch_and_i64; - break; - case 002: /* LDEOR */ - fn = tcg_gen_atomic_fetch_xor_i64; - break; - case 003: /* LDSET */ - fn = tcg_gen_atomic_fetch_or_i64; - break; - case 004: /* LDSMAX */ - fn = tcg_gen_atomic_fetch_smax_i64; - mop |= MO_SIGN; - break; - case 005: /* LDSMIN */ - fn = tcg_gen_atomic_fetch_smin_i64; - mop |= MO_SIGN; - break; - case 006: /* LDUMAX */ - fn = tcg_gen_atomic_fetch_umax_i64; - break; - case 007: /* LDUMIN */ - fn = tcg_gen_atomic_fetch_umin_i64; - break; - case 010: /* SWP */ - fn = tcg_gen_atomic_xchg_i64; - break; - case 014: /* LDAPR, LDAPRH, LDAPRB */ - if (!dc_isar_feature(aa64_rcpc_8_3, s) || - rs != 31 || a != 1 || r != 0) { - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - - mop = check_atomic_align(s, rn, mop); - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, mop); - - if (o3_opc == 014) { - /* - * LDAPR* are a special case because they are a simple load, not a - * fetch-and-do-something op. - * The architectural consistency requirements here are weaker than - * full load-acquire (we only need "load-acquire processor consistent"), - * but we choose to implement them as full LDAQ. - */ - do_gpr_ld(s, cpu_reg(s, rt), clean_addr, mop, false, - true, rt, disas_ldst_compute_iss_sf(size, false, 0), true); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - return; - } - - tcg_rs = read_cpu_reg(s, rs, true); - tcg_rt = cpu_reg(s, rt); - - if (o3_opc == 1) { /* LDCLR */ + mop = check_atomic_align(s, a->rn, mop); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + tcg_rs = read_cpu_reg(s, a->rs, true); + tcg_rt = cpu_reg(s, a->rt); + if (invert) { tcg_gen_not_i64(tcg_rs, tcg_rs); } - - /* The tcg atomic primitives are all full barriers. Therefore we + /* + * The tcg atomic primitives are all full barriers. Therefore we * can ignore the Acquire and Release bits of this instruction. */ fn(tcg_rt, clean_addr, tcg_rs, get_mem_index(s), mop); if (mop & MO_SIGN) { - switch (size) { + switch (a->sz) { case MO_8: tcg_gen_ext8u_i64(tcg_rt, tcg_rt); break; @@ -3363,6 +3282,46 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, g_assert_not_reached(); } } + return true; +} + +TRANS_FEAT(LDADD, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) +TRANS_FEAT(LDCLR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) +TRANS_FEAT(LDEOR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) +TRANS_FEAT(LDSET, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) +TRANS_FEAT(LDSMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) +TRANS_FEAT(LDSMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) +TRANS_FEAT(LDUMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) +TRANS_FEAT(LDUMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) +TRANS_FEAT(SWP, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) + +static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) +{ + bool iss_sf = ldst_iss_sf(a->sz, false, false); + TCGv_i64 clean_addr; + MemOp mop; + + if (!dc_isar_feature(aa64_atomics, s) || + !dc_isar_feature(aa64_rcpc_8_3, s)) { + return false; + } + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + mop = check_atomic_align(s, a->rn, a->sz); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + /* + * LDAPR* are a special case because they are a simple load, not a + * fetch-and-do-something op. + * The architectural consistency requirements here are weaker than + * full load-acquire (we only need "load-acquire processor consistent"), + * but we choose to implement them as full LDAQ. + */ + do_gpr_ld(s, cpu_reg(s, a->rt), clean_addr, mop, false, + true, a->rt, iss_sf, true); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return true; } /* @@ -3529,8 +3488,6 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) } switch (extract32(insn, 10, 2)) { case 0: - disas_ldst_atomic(s, insn, size, rt, is_vector); - return; case 2: break; default: From be23a049ec30aff1e3627eb1afdf095d940eae4a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:23 +0100 Subject: [PATCH 380/412] target/arm: Convert load (pointer auth) insns to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the instructions in the load/store register (pointer authentication) group ot decodetree: LDRAA, LDRAB. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-17-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 7 +++ target/arm/tcg/translate-a64.c | 83 +++++++--------------------------- 2 files changed, 23 insertions(+), 67 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 799c5ecb77..b80a17111e 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -457,3 +457,10 @@ LDUMIN .. 111 0 00 . . 1 ..... 0111 00 ..... ..... @atomic SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 + +# Load/store register (pointer authentication) + +# LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous +%ldra_imm 22:s1 12:9 !function=times_2 + +LDRA 11 111 0 00 m:1 . 1 ......... w:1 1 rn:5 rt:5 imm=%ldra_imm diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 6dc8151c40..2bffb14e84 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3324,43 +3324,23 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) return true; } -/* - * PAC memory operations - * - * 31 30 27 26 24 22 21 12 11 10 5 0 - * +------+-------+---+-----+-----+---+--------+---+---+----+-----+ - * | size | 1 1 1 | V | 0 0 | M S | 1 | imm9 | W | 1 | Rn | Rt | - * +------+-------+---+-----+-----+---+--------+---+---+----+-----+ - * - * Rt: the result register - * Rn: base address or SP - * V: vector flag (always 0 as of v8.3) - * M: clear for key DA, set for key DB - * W: pre-indexing flag - * S: sign for imm9. - */ -static void disas_ldst_pac(DisasContext *s, uint32_t insn, - int size, int rt, bool is_vector) +static bool trans_LDRA(DisasContext *s, arg_LDRA *a) { - int rn = extract32(insn, 5, 5); - bool is_wback = extract32(insn, 11, 1); - bool use_key_a = !extract32(insn, 23, 1); - int offset; TCGv_i64 clean_addr, dirty_addr, tcg_rt; MemOp memop; - if (size != 3 || is_vector || !dc_isar_feature(aa64_pauth, s)) { - unallocated_encoding(s); - return; + /* Load with pointer authentication */ + if (!dc_isar_feature(aa64_pauth, s)) { + return false; } - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - dirty_addr = read_cpu_reg_sp(s, rn, 1); + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); if (s->pauth_active) { - if (use_key_a) { + if (!a->m) { gen_helper_autda(dirty_addr, cpu_env, dirty_addr, tcg_constant_i64(0)); } else { @@ -3369,25 +3349,23 @@ static void disas_ldst_pac(DisasContext *s, uint32_t insn, } } - /* Form the 10-bit signed, scaled offset. */ - offset = (extract32(insn, 22, 1) << 9) | extract32(insn, 12, 9); - offset = sextract32(offset << size, 0, 10 + size); - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + tcg_gen_addi_i64(dirty_addr, dirty_addr, a->imm); - memop = finalize_memop(s, size); + memop = finalize_memop(s, MO_64); /* Note that "clean" and "dirty" here refer to TBI not PAC. */ clean_addr = gen_mte_check1(s, dirty_addr, false, - is_wback || rn != 31, memop); + a->w || a->rn != 31, memop); - tcg_rt = cpu_reg(s, rt); + tcg_rt = cpu_reg(s, a->rt); do_gpr_ld(s, tcg_rt, clean_addr, memop, - /* extend */ false, /* iss_valid */ !is_wback, - /* iss_srt */ rt, /* iss_sf */ true, /* iss_ar */ false); + /* extend */ false, /* iss_valid */ !a->w, + /* iss_srt */ a->rt, /* iss_sf */ true, /* iss_ar */ false); - if (is_wback) { - tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); + if (a->w) { + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); } + return true; } /* @@ -3474,31 +3452,6 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) } } -/* Load/store register (all forms) */ -static void disas_ldst_reg(DisasContext *s, uint32_t insn) -{ - int rt = extract32(insn, 0, 5); - bool is_vector = extract32(insn, 26, 1); - int size = extract32(insn, 30, 2); - - switch (extract32(insn, 24, 2)) { - case 0: - if (extract32(insn, 21, 1) == 0) { - break; - } - switch (extract32(insn, 10, 2)) { - case 0: - case 2: - break; - default: - disas_ldst_pac(s, insn, size, rt, is_vector); - return; - } - break; - } - unallocated_encoding(s); -} - /* AdvSIMD load/store multiple structures * * 31 30 29 23 22 21 16 15 12 11 10 9 5 4 0 @@ -4016,10 +3969,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) static void disas_ldst(DisasContext *s, uint32_t insn) { switch (extract32(insn, 24, 6)) { - case 0x38: case 0x39: - case 0x3c: case 0x3d: /* Load/store register (all forms) */ - disas_ldst_reg(s, insn); - break; case 0x0c: /* AdvSIMD load/store multiple structures */ disas_ldst_multiple_struct(s, insn); break; From 2521b6073b7b4b505533a941d4f9600f7585dc78 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:23 +0100 Subject: [PATCH 381/412] target/arm: Convert LDAPR/STLR (imm) to decodetree Convert the instructions in the LDAPR/STLR (unscaled immediate) group to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-18-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 10 +++ target/arm/tcg/translate-a64.c | 132 ++++++++++++--------------------- 2 files changed, 56 insertions(+), 86 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index b80a17111e..db4f44c4f4 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -464,3 +464,13 @@ LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 %ldra_imm 22:s1 12:9 !function=times_2 LDRA 11 111 0 00 m:1 . 1 ......... w:1 1 rn:5 rt:5 imm=%ldra_imm + +&ldapr_stlr_i rn rt imm sz sign ext +@ldapr_stlr_i .. ...... .. . imm:9 .. rn:5 rt:5 &ldapr_stlr_i +STLR_i sz:2 011001 00 0 ......... 00 ..... ..... @ldapr_stlr_i sign=0 ext=0 +LDAPR_i sz:2 011001 01 0 ......... 00 ..... ..... @ldapr_stlr_i sign=0 ext=0 +LDAPR_i 00 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=0 +LDAPR_i 01 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=1 +LDAPR_i 10 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=2 +LDAPR_i 00 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=0 +LDAPR_i 01 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=1 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2bffb14e84..c0d38c4879 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2652,22 +2652,12 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, } } -/* Update the Sixty-Four bit (SF) registersize. This logic is derived +/* + * Compute the ISS.SF bit for syndrome information if an exception + * is taken on a load or store. This indicates whether the instruction + * is accessing a 32-bit or 64-bit register. This logic is derived * from the ARMv8 specs for LDR (Shared decode for all encodings). */ -static bool disas_ldst_compute_iss_sf(int size, bool is_signed, int opc) -{ - int opc0 = extract32(opc, 0, 1); - int regsize; - - if (is_signed) { - regsize = opc0 ? 32 : 64; - } else { - regsize = size == 3 ? 64 : 32; - } - return regsize == 64; -} - static bool ldst_iss_sf(int size, bool sign, bool ext) { @@ -3368,88 +3358,60 @@ static bool trans_LDRA(DisasContext *s, arg_LDRA *a) return true; } -/* - * LDAPR/STLR (unscaled immediate) - * - * 31 30 24 22 21 12 10 5 0 - * +------+-------------+-----+---+--------+-----+----+-----+ - * | size | 0 1 1 0 0 1 | opc | 0 | imm9 | 0 0 | Rn | Rt | - * +------+-------------+-----+---+--------+-----+----+-----+ - * - * Rt: source or destination register - * Rn: base register - * imm9: unscaled immediate offset - * opc: 00: STLUR*, 01/10/11: various LDAPUR* - * size: size of load/store - */ -static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) +static bool trans_LDAPR_i(DisasContext *s, arg_ldapr_stlr_i *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int offset = sextract32(insn, 12, 9); - int opc = extract32(insn, 22, 2); - int size = extract32(insn, 30, 2); TCGv_i64 clean_addr, dirty_addr; - bool is_store = false; - bool extend = false; - bool iss_sf; - MemOp mop = size; + MemOp mop = a->sz | (a->sign ? MO_SIGN : 0); + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); if (!dc_isar_feature(aa64_rcpc_8_4, s)) { - unallocated_encoding(s); - return; + return false; } - switch (opc) { - case 0: /* STLURB */ - is_store = true; - break; - case 1: /* LDAPUR* */ - break; - case 2: /* LDAPURS* 64-bit variant */ - if (size == 3) { - unallocated_encoding(s); - return; - } - mop |= MO_SIGN; - break; - case 3: /* LDAPURS* 32-bit variant */ - if (size > 1) { - unallocated_encoding(s); - return; - } - mop |= MO_SIGN; - extend = true; /* zero-extend 32->64 after signed load */ - break; - default: - g_assert_not_reached(); - } - - iss_sf = disas_ldst_compute_iss_sf(size, (mop & MO_SIGN) != 0, opc); - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - mop = check_ordered_align(s, rn, offset, is_store, mop); - - dirty_addr = read_cpu_reg_sp(s, rn, 1); - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + mop = check_ordered_align(s, a->rn, a->imm, false, mop); + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + tcg_gen_addi_i64(dirty_addr, dirty_addr, a->imm); clean_addr = clean_data_tbi(s, dirty_addr); - if (is_store) { - /* Store-Release semantics */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - do_gpr_st(s, cpu_reg(s, rt), clean_addr, mop, true, rt, iss_sf, true); - } else { - /* - * Load-AcquirePC semantics; we implement as the slightly more - * restrictive Load-Acquire. - */ - do_gpr_ld(s, cpu_reg(s, rt), clean_addr, mop, - extend, true, rt, iss_sf, true); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + /* + * Load-AcquirePC semantics; we implement as the slightly more + * restrictive Load-Acquire. + */ + do_gpr_ld(s, cpu_reg(s, a->rt), clean_addr, mop, a->ext, true, + a->rt, iss_sf, true); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return true; +} + +static bool trans_STLR_i(DisasContext *s, arg_ldapr_stlr_i *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp mop = a->sz; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + + if (!dc_isar_feature(aa64_rcpc_8_4, s)) { + return false; } + + /* TODO: ARMv8.4-LSE SCTLR.nAA */ + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + mop = check_ordered_align(s, a->rn, a->imm, true, mop); + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + tcg_gen_addi_i64(dirty_addr, dirty_addr, a->imm); + clean_addr = clean_data_tbi(s, dirty_addr); + + /* Store-Release semantics */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + do_gpr_st(s, cpu_reg(s, a->rt), clean_addr, mop, true, a->rt, iss_sf, true); + return true; } /* AdvSIMD load/store multiple structures @@ -3978,8 +3940,6 @@ static void disas_ldst(DisasContext *s, uint32_t insn) case 0x19: if (extract32(insn, 21, 1) != 0) { disas_ldst_tag(s, insn); - } else if (extract32(insn, 10, 2) == 0) { - disas_ldst_ldapr_stlr(s, insn); } else { unallocated_encoding(s); } From e25ba1fa0b95316c69573bb2778f4549df097966 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:24 +0100 Subject: [PATCH 382/412] target/arm: Convert load/store (multiple structures) to decodetree Convert the instructions in the ASIMD load/store multiple structures instruction classes to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-19-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 20 +++ target/arm/tcg/translate-a64.c | 222 ++++++++++++++++----------------- 2 files changed, 131 insertions(+), 111 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index db4f44c4f4..69bdfa2e73 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -474,3 +474,23 @@ LDAPR_i 01 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext LDAPR_i 10 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=2 LDAPR_i 00 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=0 LDAPR_i 01 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=1 + +# Load/store multiple structures +# The 4-bit opcode in [15:12] encodes repeat count and structure elements +&ldst_mult rm rn rt sz q p rpt selem +@ldst_mult . q:1 ...... p:1 . . rm:5 .... sz:2 rn:5 rt:5 &ldst_mult +ST_mult 0 . 001100 . 0 0 ..... 0000 .. ..... ..... @ldst_mult rpt=1 selem=4 +ST_mult 0 . 001100 . 0 0 ..... 0010 .. ..... ..... @ldst_mult rpt=4 selem=1 +ST_mult 0 . 001100 . 0 0 ..... 0100 .. ..... ..... @ldst_mult rpt=1 selem=3 +ST_mult 0 . 001100 . 0 0 ..... 0110 .. ..... ..... @ldst_mult rpt=3 selem=1 +ST_mult 0 . 001100 . 0 0 ..... 0111 .. ..... ..... @ldst_mult rpt=1 selem=1 +ST_mult 0 . 001100 . 0 0 ..... 1000 .. ..... ..... @ldst_mult rpt=1 selem=2 +ST_mult 0 . 001100 . 0 0 ..... 1010 .. ..... ..... @ldst_mult rpt=2 selem=1 + +LD_mult 0 . 001100 . 1 0 ..... 0000 .. ..... ..... @ldst_mult rpt=1 selem=4 +LD_mult 0 . 001100 . 1 0 ..... 0010 .. ..... ..... @ldst_mult rpt=4 selem=1 +LD_mult 0 . 001100 . 1 0 ..... 0100 .. ..... ..... @ldst_mult rpt=1 selem=3 +LD_mult 0 . 001100 . 1 0 ..... 0110 .. ..... ..... @ldst_mult rpt=3 selem=1 +LD_mult 0 . 001100 . 1 0 ..... 0111 .. ..... ..... @ldst_mult rpt=1 selem=1 +LD_mult 0 . 001100 . 1 0 ..... 1000 .. ..... ..... @ldst_mult rpt=1 selem=2 +LD_mult 0 . 001100 . 1 0 ..... 1010 .. ..... ..... @ldst_mult rpt=2 selem=1 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c0d38c4879..f2d9ceeed0 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3414,99 +3414,28 @@ static bool trans_STLR_i(DisasContext *s, arg_ldapr_stlr_i *a) return true; } -/* AdvSIMD load/store multiple structures - * - * 31 30 29 23 22 21 16 15 12 11 10 9 5 4 0 - * +---+---+---------------+---+-------------+--------+------+------+------+ - * | 0 | Q | 0 0 1 1 0 0 0 | L | 0 0 0 0 0 0 | opcode | size | Rn | Rt | - * +---+---+---------------+---+-------------+--------+------+------+------+ - * - * AdvSIMD load/store multiple structures (post-indexed) - * - * 31 30 29 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---------------+---+---+---------+--------+------+------+------+ - * | 0 | Q | 0 0 1 1 0 0 1 | L | 0 | Rm | opcode | size | Rn | Rt | - * +---+---+---------------+---+---+---------+--------+------+------+------+ - * - * Rt: first (or only) SIMD&FP register to be transferred - * Rn: base address or SP - * Rm (post-index only): post-index register (when !31) or size dependent #imm - */ -static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) +static bool trans_LD_mult(DisasContext *s, arg_ldst_mult *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 10, 2); - int opcode = extract32(insn, 12, 4); - bool is_store = !extract32(insn, 22, 1); - bool is_postidx = extract32(insn, 23, 1); - bool is_q = extract32(insn, 30, 1); TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; MemOp endian, align, mop; int total; /* total bytes */ int elements; /* elements per vector */ - int rpt; /* num iterations */ - int selem; /* structure elements */ int r; + int size = a->sz; - if (extract32(insn, 31, 1) || extract32(insn, 21, 1)) { - unallocated_encoding(s); - return; + if (!a->p && a->rm != 0) { + /* For non-postindexed accesses the Rm field must be 0 */ + return false; } - - if (!is_postidx && rm != 0) { - unallocated_encoding(s); - return; + if (size == 3 && !a->q && a->selem != 1) { + return false; } - - /* From the shared decode logic */ - switch (opcode) { - case 0x0: - rpt = 1; - selem = 4; - break; - case 0x2: - rpt = 4; - selem = 1; - break; - case 0x4: - rpt = 1; - selem = 3; - break; - case 0x6: - rpt = 3; - selem = 1; - break; - case 0x7: - rpt = 1; - selem = 1; - break; - case 0x8: - rpt = 1; - selem = 2; - break; - case 0xa: - rpt = 2; - selem = 1; - break; - default: - unallocated_encoding(s); - return; - } - - if (size == 3 && !is_q && selem != 1) { - /* reserved */ - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; + return true; } - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } @@ -3516,22 +3445,22 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) endian = MO_LE; } - total = rpt * selem * (is_q ? 16 : 8); - tcg_rn = cpu_reg_sp(s, rn); + total = a->rpt * a->selem * (a->q ? 16 : 8); + tcg_rn = cpu_reg_sp(s, a->rn); /* * Issue the MTE check vs the logical repeat count, before we * promote consecutive little-endian elements below. */ - clean_addr = gen_mte_checkN(s, tcg_rn, is_store, is_postidx || rn != 31, - total, finalize_memop_asimd(s, size)); + clean_addr = gen_mte_checkN(s, tcg_rn, false, a->p || a->rn != 31, total, + finalize_memop_asimd(s, size)); /* * Consecutive little-endian elements from a single register * can be promoted to a larger little-endian operation. */ align = MO_ALIGN; - if (selem == 1 && endian == MO_LE) { + if (a->selem == 1 && endian == MO_LE) { align = pow2_align(size); size = 3; } @@ -3540,45 +3469,119 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) } mop = endian | size | align; - elements = (is_q ? 16 : 8) >> size; + elements = (a->q ? 16 : 8) >> size; tcg_ebytes = tcg_constant_i64(1 << size); - for (r = 0; r < rpt; r++) { + for (r = 0; r < a->rpt; r++) { int e; for (e = 0; e < elements; e++) { int xs; - for (xs = 0; xs < selem; xs++) { - int tt = (rt + r + xs) % 32; - if (is_store) { - do_vec_st(s, tt, e, clean_addr, mop); - } else { - do_vec_ld(s, tt, e, clean_addr, mop); - } + for (xs = 0; xs < a->selem; xs++) { + int tt = (a->rt + r + xs) % 32; + do_vec_ld(s, tt, e, clean_addr, mop); tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); } } } - if (!is_store) { - /* For non-quad operations, setting a slice of the low - * 64 bits of the register clears the high 64 bits (in - * the ARM ARM pseudocode this is implicit in the fact - * that 'rval' is a 64 bit wide variable). - * For quad operations, we might still need to zero the - * high bits of SVE. - */ - for (r = 0; r < rpt * selem; r++) { - int tt = (rt + r) % 32; - clear_vec_high(s, is_q, tt); + /* + * For non-quad operations, setting a slice of the low 64 bits of + * the register clears the high 64 bits (in the ARM ARM pseudocode + * this is implicit in the fact that 'rval' is a 64 bit wide + * variable). For quad operations, we might still need to zero + * the high bits of SVE. + */ + for (r = 0; r < a->rpt * a->selem; r++) { + int tt = (a->rt + r) % 32; + clear_vec_high(s, a->q, tt); + } + + if (a->p) { + if (a->rm == 31) { + tcg_gen_addi_i64(tcg_rn, tcg_rn, total); + } else { + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); + } + } + return true; +} + +static bool trans_ST_mult(DisasContext *s, arg_ldst_mult *a) +{ + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp endian, align, mop; + + int total; /* total bytes */ + int elements; /* elements per vector */ + int r; + int size = a->sz; + + if (!a->p && a->rm != 0) { + /* For non-postindexed accesses the Rm field must be 0 */ + return false; + } + if (size == 3 && !a->q && a->selem != 1) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + /* For our purposes, bytes are always little-endian. */ + endian = s->be_data; + if (size == 0) { + endian = MO_LE; + } + + total = a->rpt * a->selem * (a->q ? 16 : 8); + tcg_rn = cpu_reg_sp(s, a->rn); + + /* + * Issue the MTE check vs the logical repeat count, before we + * promote consecutive little-endian elements below. + */ + clean_addr = gen_mte_checkN(s, tcg_rn, true, a->p || a->rn != 31, total, + finalize_memop_asimd(s, size)); + + /* + * Consecutive little-endian elements from a single register + * can be promoted to a larger little-endian operation. + */ + align = MO_ALIGN; + if (a->selem == 1 && endian == MO_LE) { + align = pow2_align(size); + size = 3; + } + if (!s->align_mem) { + align = 0; + } + mop = endian | size | align; + + elements = (a->q ? 16 : 8) >> size; + tcg_ebytes = tcg_constant_i64(1 << size); + for (r = 0; r < a->rpt; r++) { + int e; + for (e = 0; e < elements; e++) { + int xs; + for (xs = 0; xs < a->selem; xs++) { + int tt = (a->rt + r + xs) % 32; + do_vec_st(s, tt, e, clean_addr, mop); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); + } } } - if (is_postidx) { - if (rm == 31) { + if (a->p) { + if (a->rm == 31) { tcg_gen_addi_i64(tcg_rn, tcg_rn, total); } else { - tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); } } + return true; } /* AdvSIMD load/store single structure @@ -3931,9 +3934,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) static void disas_ldst(DisasContext *s, uint32_t insn) { switch (extract32(insn, 24, 6)) { - case 0x0c: /* AdvSIMD load/store multiple structures */ - disas_ldst_multiple_struct(s, insn); - break; case 0x0d: /* AdvSIMD load/store single structure */ disas_ldst_single_struct(s, insn); break; From 3d507213264f3af221598f9af12b5e6212bf403a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:24 +0100 Subject: [PATCH 383/412] target/arm: Convert load/store single structure to decodetree Convert the ASIMD load/store single structure insns to decodetree. Signed-off-by: Peter Maydell Message-id: 20230602155223.2040685-20-peter.maydell@linaro.org Reviewed-by: Richard Henderson --- target/arm/tcg/a64.decode | 34 +++++ target/arm/tcg/translate-a64.c | 219 +++++++++++++++------------------ 2 files changed, 136 insertions(+), 117 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 69bdfa2e73..4ffdc91865 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -494,3 +494,37 @@ LD_mult 0 . 001100 . 1 0 ..... 0110 .. ..... ..... @ldst_mult rpt=3 sele LD_mult 0 . 001100 . 1 0 ..... 0111 .. ..... ..... @ldst_mult rpt=1 selem=1 LD_mult 0 . 001100 . 1 0 ..... 1000 .. ..... ..... @ldst_mult rpt=1 selem=2 LD_mult 0 . 001100 . 1 0 ..... 1010 .. ..... ..... @ldst_mult rpt=2 selem=1 + +# Load/store single structure +&ldst_single rm rn rt p selem index scale + +%ldst_single_selem 13:1 21:1 !function=plus_1 + +%ldst_single_index_b 30:1 10:3 +%ldst_single_index_h 30:1 11:2 +%ldst_single_index_s 30:1 12:1 + +@ldst_single_b .. ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=0 selem=%ldst_single_selem \ + index=%ldst_single_index_b +@ldst_single_h .. ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=1 selem=%ldst_single_selem \ + index=%ldst_single_index_h +@ldst_single_s .. ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=2 selem=%ldst_single_selem \ + index=%ldst_single_index_s +@ldst_single_d . index:1 ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=3 selem=%ldst_single_selem + +ST_single 0 . 001101 . 0 . ..... 00 . ... ..... ..... @ldst_single_b +ST_single 0 . 001101 . 0 . ..... 01 . ..0 ..... ..... @ldst_single_h +ST_single 0 . 001101 . 0 . ..... 10 . .00 ..... ..... @ldst_single_s +ST_single 0 . 001101 . 0 . ..... 10 . 001 ..... ..... @ldst_single_d + +LD_single 0 . 001101 . 1 . ..... 00 . ... ..... ..... @ldst_single_b +LD_single 0 . 001101 . 1 . ..... 01 . ..0 ..... ..... @ldst_single_h +LD_single 0 . 001101 . 1 . ..... 10 . .00 ..... ..... @ldst_single_s +LD_single 0 . 001101 . 1 . ..... 10 . 001 ..... ..... @ldst_single_d + +# Replicating load case +LD_single_repl 0 q:1 001101 p:1 1 . rm:5 11 . 0 scale:2 rn:5 rt:5 selem=%ldst_single_selem diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f2d9ceeed0..f9a76141eb 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3584,141 +3584,129 @@ static bool trans_ST_mult(DisasContext *s, arg_ldst_mult *a) return true; } -/* AdvSIMD load/store single structure - * - * 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0 - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * | 0 | Q | 0 0 1 1 0 1 0 | L R | 0 0 0 0 0 | opc | S | size | Rn | Rt | - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * - * AdvSIMD load/store single structure (post-indexed) - * - * 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0 - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * | 0 | Q | 0 0 1 1 0 1 1 | L R | Rm | opc | S | size | Rn | Rt | - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * - * Rt: first (or only) SIMD&FP register to be transferred - * Rn: base address or SP - * Rm (post-index only): post-index register (when !31) or size dependent #imm - * index = encoded in Q:S:size dependent on size - * - * lane_size = encoded in R, opc - * transfer width = encoded in opc, S, size - */ -static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) +static bool trans_ST_single(DisasContext *s, arg_ldst_single *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 10, 2); - int S = extract32(insn, 12, 1); - int opc = extract32(insn, 13, 3); - int R = extract32(insn, 21, 1); - int is_load = extract32(insn, 22, 1); - int is_postidx = extract32(insn, 23, 1); - int is_q = extract32(insn, 30, 1); - - int scale = extract32(opc, 1, 2); - int selem = (extract32(opc, 0, 1) << 1 | R) + 1; - bool replicate = false; - int index = is_q << 3 | S << 2 | size; - int xs, total; + int xs, total, rt; TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; MemOp mop; - if (extract32(insn, 31, 1)) { - unallocated_encoding(s); - return; + if (!a->p && a->rm != 0) { + return false; } - if (!is_postidx && rm != 0) { - unallocated_encoding(s); - return; - } - - switch (scale) { - case 3: - if (!is_load || S) { - unallocated_encoding(s); - return; - } - scale = size; - replicate = true; - break; - case 0: - break; - case 1: - if (extract32(size, 0, 1)) { - unallocated_encoding(s); - return; - } - index >>= 1; - break; - case 2: - if (extract32(size, 1, 1)) { - unallocated_encoding(s); - return; - } - if (!extract32(size, 0, 1)) { - index >>= 2; - } else { - if (S) { - unallocated_encoding(s); - return; - } - index >>= 3; - scale = 3; - } - break; - default: - g_assert_not_reached(); - } - if (!fp_access_check(s)) { - return; + return true; } - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - total = selem << scale; - tcg_rn = cpu_reg_sp(s, rn); + total = a->selem << a->scale; + tcg_rn = cpu_reg_sp(s, a->rn); - mop = finalize_memop_asimd(s, scale); - - clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31, + mop = finalize_memop_asimd(s, a->scale); + clean_addr = gen_mte_checkN(s, tcg_rn, true, a->p || a->rn != 31, total, mop); - tcg_ebytes = tcg_constant_i64(1 << scale); - for (xs = 0; xs < selem; xs++) { - if (replicate) { - /* Load and replicate to all elements */ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr, get_mem_index(s), mop); - tcg_gen_gvec_dup_i64(scale, vec_full_reg_offset(s, rt), - (is_q + 1) * 8, vec_full_reg_size(s), - tcg_tmp); - } else { - /* Load/store one element per register */ - if (is_load) { - do_vec_ld(s, rt, index, clean_addr, mop); - } else { - do_vec_st(s, rt, index, clean_addr, mop); - } - } + tcg_ebytes = tcg_constant_i64(1 << a->scale); + for (xs = 0, rt = a->rt; xs < a->selem; xs++, rt = (rt + 1) % 32) { + do_vec_st(s, rt, a->index, clean_addr, mop); tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); - rt = (rt + 1) % 32; } - if (is_postidx) { - if (rm == 31) { + if (a->p) { + if (a->rm == 31) { tcg_gen_addi_i64(tcg_rn, tcg_rn, total); } else { - tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); } } + return true; +} + +static bool trans_LD_single(DisasContext *s, arg_ldst_single *a) +{ + int xs, total, rt; + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp mop; + + if (!a->p && a->rm != 0) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + total = a->selem << a->scale; + tcg_rn = cpu_reg_sp(s, a->rn); + + mop = finalize_memop_asimd(s, a->scale); + clean_addr = gen_mte_checkN(s, tcg_rn, false, a->p || a->rn != 31, + total, mop); + + tcg_ebytes = tcg_constant_i64(1 << a->scale); + for (xs = 0, rt = a->rt; xs < a->selem; xs++, rt = (rt + 1) % 32) { + do_vec_ld(s, rt, a->index, clean_addr, mop); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); + } + + if (a->p) { + if (a->rm == 31) { + tcg_gen_addi_i64(tcg_rn, tcg_rn, total); + } else { + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); + } + } + return true; +} + +static bool trans_LD_single_repl(DisasContext *s, arg_LD_single_repl *a) +{ + int xs, total, rt; + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp mop; + + if (!a->p && a->rm != 0) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + total = a->selem << a->scale; + tcg_rn = cpu_reg_sp(s, a->rn); + + mop = finalize_memop_asimd(s, a->scale); + clean_addr = gen_mte_checkN(s, tcg_rn, false, a->p || a->rn != 31, + total, mop); + + tcg_ebytes = tcg_constant_i64(1 << a->scale); + for (xs = 0, rt = a->rt; xs < a->selem; xs++, rt = (rt + 1) % 32) { + /* Load and replicate to all elements */ + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + + tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr, get_mem_index(s), mop); + tcg_gen_gvec_dup_i64(a->scale, vec_full_reg_offset(s, rt), + (a->q + 1) * 8, vec_full_reg_size(s), tcg_tmp); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); + } + + if (a->p) { + if (a->rm == 31) { + tcg_gen_addi_i64(tcg_rn, tcg_rn, total); + } else { + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); + } + } + return true; } /* @@ -3934,9 +3922,6 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) static void disas_ldst(DisasContext *s, uint32_t insn) { switch (extract32(insn, 24, 6)) { - case 0x0d: /* AdvSIMD load/store single structure */ - disas_ldst_single_struct(s, insn); - break; case 0x19: if (extract32(insn, 21, 1) != 0) { disas_ldst_tag(s, insn); From 946ccfd590f65c09211c4899446e07ef589bc093 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 Jun 2023 11:20:24 +0100 Subject: [PATCH 384/412] target/arm: Convert load/store tags insns to decodetree Convert the instructions in the load/store memory tags instruction group to decodetree. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230602155223.2040685-21-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 25 +++ target/arm/tcg/translate-a64.c | 360 ++++++++++++++++----------------- 2 files changed, 199 insertions(+), 186 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 4ffdc91865..ef64a3f9cb 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -528,3 +528,28 @@ LD_single 0 . 001101 . 1 . ..... 10 . 001 ..... ..... @ldst_single_d # Replicating load case LD_single_repl 0 q:1 001101 p:1 1 . rm:5 11 . 0 scale:2 rn:5 rt:5 selem=%ldst_single_selem + +%tag_offset 12:s9 !function=scale_by_log2_tag_granule +&ldst_tag rn rt imm p w +@ldst_tag ........ .. . ......... .. rn:5 rt:5 &ldst_tag imm=%tag_offset +@ldst_tag_mult ........ .. . 000000000 .. rn:5 rt:5 &ldst_tag imm=0 + +STZGM 11011001 00 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 +STG 11011001 00 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +STG 11011001 00 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +STG 11011001 00 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +LDG 11011001 01 1 ......... 00 ..... ..... @ldst_tag p=0 w=0 +STZG 11011001 01 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +STZG 11011001 01 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +STZG 11011001 01 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +STGM 11011001 10 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 +ST2G 11011001 10 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +ST2G 11011001 10 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +ST2G 11011001 10 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +LDGM 11011001 11 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 +STZ2G 11011001 11 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +STZ2G 11011001 11 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +STZ2G 11011001 11 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f9a76141eb..3baab6aa60 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -62,6 +62,12 @@ static int uimm_scaled(DisasContext *s, int x) return imm << scale; } +/* For load/store memory tags: scale offset by LOG2_TAG_GRANULE */ +static int scale_by_log2_tag_granule(DisasContext *s, int x) +{ + return x << LOG2_TAG_GRANULE; +} + /* * Include the generated decoders. */ @@ -3709,185 +3715,184 @@ static bool trans_LD_single_repl(DisasContext *s, arg_LD_single_repl *a) return true; } -/* - * Load/Store memory tags - * - * 31 30 29 24 22 21 12 10 5 0 - * +-----+-------------+-----+---+------+-----+------+------+ - * | 1 1 | 0 1 1 0 0 1 | op1 | 1 | imm9 | op2 | Rn | Rt | - * +-----+-------------+-----+---+------+-----+------+------+ - */ -static void disas_ldst_tag(DisasContext *s, uint32_t insn) +static bool trans_STZGM(DisasContext *s, arg_ldst_tag *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - uint64_t offset = sextract64(insn, 12, 9) << LOG2_TAG_GRANULE; - int op2 = extract32(insn, 10, 2); - int op1 = extract32(insn, 22, 2); - bool is_load = false, is_pair = false, is_zero = false, is_mult = false; - int index = 0; TCGv_i64 addr, clean_addr, tcg_rt; + int size = 4 << s->dcz_blocksize; - /* We checked insn bits [29:24,21] in the caller. */ - if (extract32(insn, 30, 2) != 3) { - goto do_unallocated; + if (!dc_isar_feature(aa64_mte, s)) { + return false; + } + if (s->current_el == 0) { + return false; } - /* - * @index is a tri-state variable which has 3 states: - * < 0 : post-index, writeback - * = 0 : signed offset - * > 0 : pre-index, writeback - */ - switch (op1) { - case 0: - if (op2 != 0) { - /* STG */ - index = op2 - 2; - } else { - /* STZGM */ - if (s->current_el == 0 || offset != 0) { - goto do_unallocated; - } - is_mult = is_zero = true; - } - break; - case 1: - if (op2 != 0) { - /* STZG */ - is_zero = true; - index = op2 - 2; - } else { - /* LDG */ - is_load = true; - } - break; - case 2: - if (op2 != 0) { - /* ST2G */ - is_pair = true; - index = op2 - 2; - } else { - /* STGM */ - if (s->current_el == 0 || offset != 0) { - goto do_unallocated; - } - is_mult = true; - } - break; - case 3: - if (op2 != 0) { - /* STZ2G */ - is_pair = is_zero = true; - index = op2 - 2; - } else { - /* LDGM */ - if (s->current_el == 0 || offset != 0) { - goto do_unallocated; - } - is_mult = is_load = true; - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - return; - } - - if (is_mult - ? !dc_isar_feature(aa64_mte, s) - : !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - addr = read_cpu_reg_sp(s, rn, true); - if (index >= 0) { + addr = read_cpu_reg_sp(s, a->rn, true); + tcg_gen_addi_i64(addr, addr, a->imm); + tcg_rt = cpu_reg(s, a->rt); + + if (s->ata) { + gen_helper_stzgm_tags(cpu_env, addr, tcg_rt); + } + /* + * The non-tags portion of STZGM is mostly like DC_ZVA, + * except the alignment happens before the access. + */ + clean_addr = clean_data_tbi(s, addr); + tcg_gen_andi_i64(clean_addr, clean_addr, -size); + gen_helper_dc_zva(cpu_env, clean_addr); + return true; +} + +static bool trans_STGM(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + + if (!dc_isar_feature(aa64_mte, s)) { + return false; + } + if (s->current_el == 0) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + tcg_gen_addi_i64(addr, addr, a->imm); + tcg_rt = cpu_reg(s, a->rt); + + if (s->ata) { + gen_helper_stgm(cpu_env, addr, tcg_rt); + } else { + MMUAccessType acc = MMU_DATA_STORE; + int size = 4 << GMID_EL1_BS; + + clean_addr = clean_data_tbi(s, addr); + tcg_gen_andi_i64(clean_addr, clean_addr, -size); + gen_probe_access(s, clean_addr, acc, size); + } + return true; +} + +static bool trans_LDGM(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + + if (!dc_isar_feature(aa64_mte, s)) { + return false; + } + if (s->current_el == 0) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + tcg_gen_addi_i64(addr, addr, a->imm); + tcg_rt = cpu_reg(s, a->rt); + + if (s->ata) { + gen_helper_ldgm(tcg_rt, cpu_env, addr); + } else { + MMUAccessType acc = MMU_DATA_LOAD; + int size = 4 << GMID_EL1_BS; + + clean_addr = clean_data_tbi(s, addr); + tcg_gen_andi_i64(clean_addr, clean_addr, -size); + gen_probe_access(s, clean_addr, acc, size); + /* The result tags are zeros. */ + tcg_gen_movi_i64(tcg_rt, 0); + } + return true; +} + +static bool trans_LDG(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + + if (!dc_isar_feature(aa64_mte_insn_reg, s)) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + if (!a->p) { /* pre-index or signed offset */ - tcg_gen_addi_i64(addr, addr, offset); + tcg_gen_addi_i64(addr, addr, a->imm); } - if (is_mult) { - tcg_rt = cpu_reg(s, rt); + tcg_gen_andi_i64(addr, addr, -TAG_GRANULE); + tcg_rt = cpu_reg(s, a->rt); + if (s->ata) { + gen_helper_ldg(tcg_rt, cpu_env, addr, tcg_rt); + } else { + /* + * Tag access disabled: we must check for aborts on the load + * load from [rn+offset], and then insert a 0 tag into rt. + */ + clean_addr = clean_data_tbi(s, addr); + gen_probe_access(s, clean_addr, MMU_DATA_LOAD, MO_8); + gen_address_with_allocation_tag0(tcg_rt, tcg_rt); + } - if (is_zero) { - int size = 4 << s->dcz_blocksize; - - if (s->ata) { - gen_helper_stzgm_tags(cpu_env, addr, tcg_rt); - } - /* - * The non-tags portion of STZGM is mostly like DC_ZVA, - * except the alignment happens before the access. - */ - clean_addr = clean_data_tbi(s, addr); - tcg_gen_andi_i64(clean_addr, clean_addr, -size); - gen_helper_dc_zva(cpu_env, clean_addr); - } else if (s->ata) { - if (is_load) { - gen_helper_ldgm(tcg_rt, cpu_env, addr); - } else { - gen_helper_stgm(cpu_env, addr, tcg_rt); - } - } else { - MMUAccessType acc = is_load ? MMU_DATA_LOAD : MMU_DATA_STORE; - int size = 4 << GMID_EL1_BS; - - clean_addr = clean_data_tbi(s, addr); - tcg_gen_andi_i64(clean_addr, clean_addr, -size); - gen_probe_access(s, clean_addr, acc, size); - - if (is_load) { - /* The result tags are zeros. */ - tcg_gen_movi_i64(tcg_rt, 0); - } + if (a->w) { + /* pre-index or post-index */ + if (a->p) { + /* post-index */ + tcg_gen_addi_i64(addr, addr, a->imm); } - return; + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), addr); + } + return true; +} + +static bool do_STG(DisasContext *s, arg_ldst_tag *a, bool is_zero, bool is_pair) +{ + TCGv_i64 addr, tcg_rt; + + if (a->rn == 31) { + gen_check_sp_alignment(s); } - if (is_load) { - tcg_gen_andi_i64(addr, addr, -TAG_GRANULE); - tcg_rt = cpu_reg(s, rt); - if (s->ata) { - gen_helper_ldg(tcg_rt, cpu_env, addr, tcg_rt); + addr = read_cpu_reg_sp(s, a->rn, true); + if (!a->p) { + /* pre-index or signed offset */ + tcg_gen_addi_i64(addr, addr, a->imm); + } + tcg_rt = cpu_reg_sp(s, a->rt); + if (!s->ata) { + /* + * For STG and ST2G, we need to check alignment and probe memory. + * TODO: For STZG and STZ2G, we could rely on the stores below, + * at least for system mode; user-only won't enforce alignment. + */ + if (is_pair) { + gen_helper_st2g_stub(cpu_env, addr); } else { - /* - * Tag access disabled: we must check for aborts on the load - * load from [rn+offset], and then insert a 0 tag into rt. - */ - clean_addr = clean_data_tbi(s, addr); - gen_probe_access(s, clean_addr, MMU_DATA_LOAD, MO_8); - gen_address_with_allocation_tag0(tcg_rt, tcg_rt); + gen_helper_stg_stub(cpu_env, addr); + } + } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { + if (is_pair) { + gen_helper_st2g_parallel(cpu_env, addr, tcg_rt); + } else { + gen_helper_stg_parallel(cpu_env, addr, tcg_rt); } } else { - tcg_rt = cpu_reg_sp(s, rt); - if (!s->ata) { - /* - * For STG and ST2G, we need to check alignment and probe memory. - * TODO: For STZG and STZ2G, we could rely on the stores below, - * at least for system mode; user-only won't enforce alignment. - */ - if (is_pair) { - gen_helper_st2g_stub(cpu_env, addr); - } else { - gen_helper_stg_stub(cpu_env, addr); - } - } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - if (is_pair) { - gen_helper_st2g_parallel(cpu_env, addr, tcg_rt); - } else { - gen_helper_stg_parallel(cpu_env, addr, tcg_rt); - } + if (is_pair) { + gen_helper_st2g(cpu_env, addr, tcg_rt); } else { - if (is_pair) { - gen_helper_st2g(cpu_env, addr, tcg_rt); - } else { - gen_helper_stg(cpu_env, addr, tcg_rt); - } + gen_helper_stg(cpu_env, addr, tcg_rt); } } @@ -3908,32 +3913,21 @@ static void disas_ldst_tag(DisasContext *s, uint32_t insn) } } - if (index != 0) { + if (a->w) { /* pre-index or post-index */ - if (index < 0) { + if (a->p) { /* post-index */ - tcg_gen_addi_i64(addr, addr, offset); + tcg_gen_addi_i64(addr, addr, a->imm); } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), addr); + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), addr); } + return true; } -/* Loads and stores */ -static void disas_ldst(DisasContext *s, uint32_t insn) -{ - switch (extract32(insn, 24, 6)) { - case 0x19: - if (extract32(insn, 21, 1) != 0) { - disas_ldst_tag(s, insn); - } else { - unallocated_encoding(s); - } - break; - default: - unallocated_encoding(s); - break; - } -} +TRANS_FEAT(STG, aa64_mte_insn_reg, do_STG, a, false, false) +TRANS_FEAT(STZG, aa64_mte_insn_reg, do_STG, a, true, false) +TRANS_FEAT(ST2G, aa64_mte_insn_reg, do_STG, a, false, true) +TRANS_FEAT(STZ2G, aa64_mte_insn_reg, do_STG, a, true, true) typedef void ArithTwoOp(TCGv_i64, TCGv_i64, TCGv_i64); @@ -13829,12 +13823,6 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) static void disas_a64_legacy(DisasContext *s, uint32_t insn) { switch (extract32(insn, 25, 4)) { - case 0x4: - case 0x6: - case 0xc: - case 0xe: /* Loads and stores */ - disas_ldst(s, insn); - break; case 0x5: case 0xd: /* Data processing - register */ disas_data_proc_reg(s, insn); From f837b468cdaa7e736b5385c7dc4f8c5adcad3bf1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 6 Jun 2023 11:46:08 +0100 Subject: [PATCH 385/412] hw/intc/allwinner-a10-pic: Handle IRQ levels other than 0 or 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 2c5fa0778c3b430 we fixed an endianness bug in the Allwinner A10 PIC model; however in the process we introduced a regression. This is because the old code was robust against the incoming 'level' argument being something other than 0 or 1, whereas the new code was not. In particular, the allwinner-sdhost code treats its IRQ line as 0-vs-non-0 rather than 0-vs-1, so when the SD controller set its IRQ line for any reason other than transmit the interrupt controller would ignore it. The observed effect was a guest timeout when rebooting the guest kernel. Handle level values other than 0 or 1, to restore the old behaviour. Fixes: 2c5fa0778c3b430 ("hw/intc/allwinner-a10-pic: Don't use set_bit()/clear_bit()") Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Guenter Roeck Message-id: 20230606104609.3692557-2-peter.maydell@linaro.org --- hw/intc/allwinner-a10-pic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index 4875e68ba6..d0bf8d545b 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -51,7 +51,7 @@ static void aw_a10_pic_set_irq(void *opaque, int irq, int level) AwA10PICState *s = opaque; uint32_t *pending_reg = &s->irq_pending[irq / 32]; - *pending_reg = deposit32(*pending_reg, irq % 32, 1, level); + *pending_reg = deposit32(*pending_reg, irq % 32, 1, !!level); aw_a10_pic_update(s); } From 22c81783c9458f2b41d554e0172c9d0fcf6da4cc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 6 Jun 2023 11:46:09 +0100 Subject: [PATCH 386/412] hw/sd/allwinner-sdhost: Don't send non-boolean IRQ line levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU allows qemu_irq lines to transfer arbitrary integers. However the convention is that for a simple IRQ line the values transferred are always 0 and 1. The A10 SD controller device instead assumes a 0-vs-non-0 convention, which happens to work with the interrupt controller it is wired up to. Coerce the value to boolean to follow our usual convention. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Guenter Roeck Message-id: 20230606104609.3692557-3-peter.maydell@linaro.org --- hw/sd/allwinner-sdhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index 286e009509..1a576d62ae 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -193,7 +193,7 @@ static void allwinner_sdhost_update_irq(AwSdHostState *s) } trace_allwinner_sdhost_update_irq(irq); - qemu_set_irq(s->irq, irq); + qemu_set_irq(s->irq, !!irq); } static void allwinner_sdhost_update_transfer_cnt(AwSdHostState *s, From d2f9a79a8cf6ab992e1d0f27ad05b3e582d2b18a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 6 Jun 2023 14:49:17 +0100 Subject: [PATCH 387/412] hw/timer/nrf51_timer: Don't lose time when timer is queried in tight loop The nrf51_timer has a free-running counter which we implement using the pattern of using two fields (update_counter_ns, counter) to track the last point at which we calculated the counter value, and the counter value at that time. Then we can find the current counter value by converting the difference in wall-clock time between then and now to a tick count that we need to add to the counter value. Unfortunately the nrf51_timer's implementation of this has a bug which means it loses time every time update_counter() is called. After updating s->counter it always sets s->update_counter_ns to 'now', even though the actual point when s->counter hit the new value will be some point in the past (half a tick, say). In the worst case (guest code in a tight loop reading the counter, icount mode) the counter is continually queried less than a tick after it was last read, so s->counter never advances but s->update_counter_ns does, and the guest never makes forward progress. The fix for this is to only advance update_counter_ns to the timestamp of the last tick, not all the way to 'now'. (This is the pattern used in hw/misc/mps2-fpgaio.c's counter.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Joel Stanley Message-id: 20230606134917.3782215-1-peter.maydell@linaro.org --- hw/timer/nrf51_timer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c index 42be79c736..50c6772383 100644 --- a/hw/timer/nrf51_timer.c +++ b/hw/timer/nrf51_timer.c @@ -45,7 +45,12 @@ static uint32_t update_counter(NRF51TimerState *s, int64_t now) uint32_t ticks = ns_to_ticks(s, now - s->update_counter_ns); s->counter = (s->counter + ticks) % BIT(bitwidths[s->bitmode]); - s->update_counter_ns = now; + /* + * Only advance the sync time to the timestamp of the last tick, + * not all the way to 'now', so we don't lose time if we do + * multiple resyncs in a single tick. + */ + s->update_counter_ns += ticks_to_ns(s, ticks); return ticks; } From 93faf3b9c9396bc06da4a380de67637334b1143d Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Wed, 7 Jun 2023 11:21:12 +0200 Subject: [PATCH 388/412] hw/arm/Kconfig: sbsa-ref uses Bochs display Signed-off-by: Marcin Juszkiewicz Reviewed-by: Thomas Huth Message-id: 20230607092112.655098-1-marcin.juszkiewicz@linaro.org Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2159de3ce6..7de17d1e8c 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -268,6 +268,7 @@ config SBSA_REF select PL061 # GPIO select USB_EHCI_SYSBUS select WDT_SBSA + select BOCHS_DISPLAY config SABRELITE bool From bd96e10071e4d44eea9b9c8888ed12d2f9d8fae9 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 15 Jun 2023 15:22:56 +0100 Subject: [PATCH 389/412] imx_serial: set wake bit when we receive a data byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Linux kernel added a flood check for RX data recently in commit 496a4471b7c3 ("serial: imx: work-around for hardware RX flood"). This check uses the wake bit in the UART status register 2. The wake bit indicates that the receiver detected a start bit on the RX line. If the kernel sees a number of RX interrupts without the wake bit being set, it treats this as spurious data and resets the UART port. imx_serial does never set the wake bit and triggers the kernel's flood check. This patch adds support for the wake bit. wake is set when we receive a new character (it's not set for break events). It seems that wake is cleared by the kernel driver, the hardware does not have to clear it automatically after data was read. The wake bit can be configured as an interrupt source. Support this mechanism as well. Co-developed-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Martin Kaiser Signed-off-by: Peter Maydell --- hw/char/imx_serial.c | 5 ++++- include/hw/char/imx_serial.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index ee1375e26d..1b75a89588 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -80,7 +80,7 @@ static void imx_update(IMXSerialState *s) * TCEN and TXDC are both bit 3 * RDR and DREN are both bit 0 */ - mask |= s->ucr4 & (UCR4_TCEN | UCR4_DREN); + mask |= s->ucr4 & (UCR4_WKEN | UCR4_TCEN | UCR4_DREN); usr2 = s->usr2 & mask; @@ -321,6 +321,9 @@ static void imx_put_data(void *opaque, uint32_t value) static void imx_receive(void *opaque, const uint8_t *buf, int size) { + IMXSerialState *s = (IMXSerialState *)opaque; + + s->usr2 |= USR2_WAKE; imx_put_data(opaque, *buf); } diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h index 91c9894ad5..b823f94519 100644 --- a/include/hw/char/imx_serial.h +++ b/include/hw/char/imx_serial.h @@ -71,6 +71,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(IMXSerialState, IMX_SERIAL) #define UCR4_DREN BIT(0) /* Receive Data Ready interrupt enable */ #define UCR4_TCEN BIT(3) /* TX complete interrupt enable */ +#define UCR4_WKEN BIT(7) /* WAKE interrupt enable */ #define UTS1_TXEMPTY (1<<6) #define UTS1_RXEMPTY (1<<5) From ff49fb950dd89da21b6ab77f39d7bd5be2a63542 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Wed, 31 May 2023 19:18:34 +0200 Subject: [PATCH 390/412] docs: sbsa: document board to firmware interface We plan to add more hardware information into DeviceTree to limit amount of hardcoded values in firmware. Signed-off-by: Marcin Juszkiewicz Message-id: 20230531171834.236569-1-marcin.juszkiewicz@linaro.org [PMM: fix format nits, add text about platform version fields from a comment in the C source file] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/sbsa.rst | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/docs/system/arm/sbsa.rst b/docs/system/arm/sbsa.rst index 016776aed8..f571fe645e 100644 --- a/docs/system/arm/sbsa.rst +++ b/docs/system/arm/sbsa.rst @@ -6,12 +6,7 @@ any real hardware the ``sbsa-ref`` board intends to look like real hardware. The `Server Base System Architecture `_ defines a minimum base line of hardware support and importantly how the firmware -reports that to any operating system. It is a static system that -reports a very minimal DT to the firmware for non-discoverable -information about components affected by the qemu command line (i.e. -cpus and memory). As a result it must have a firmware specifically -built to expect a certain hardware layout (as you would in a real -machine). +reports that to any operating system. It is intended to be a machine for developing firmware and testing standards compliance with operating systems. @@ -19,7 +14,7 @@ standards compliance with operating systems. Supported devices """"""""""""""""" -The sbsa-ref board supports: +The ``sbsa-ref`` board supports: - A configurable number of AArch64 CPUs - GIC version 3 @@ -30,3 +25,32 @@ The sbsa-ref board supports: - Bochs display adapter on PCIe bus - A generic SBSA watchdog device + +Board to firmware interface +""""""""""""""""""""""""""" + +``sbsa-ref`` is a static system that reports a very minimal devicetree to the +firmware for non-discoverable information about system components. This +includes both internal hardware and parts affected by the qemu command line +(i.e. CPUs and memory). As a result it must have a firmware specifically built +to expect a certain hardware layout (as you would in a real machine). + +DeviceTree information +'''''''''''''''''''''' + +The devicetree provided by the board model to the firmware is not intended +to be a complete compliant DT. It currently reports: + + - CPUs + - memory + - platform version + - GIC addresses + +The platform version is only for informing platform firmware about +what kind of ``sbsa-ref`` board it is running on. It is neither +a QEMU versioned machine type nor a reflection of the level of the +SBSA/SystemReady SR support provided. + +The ``machine-version-major`` value is updated when changes breaking +fw compatibility are introduced. The ``machine-version-minor`` value +is updated when features are added that don't break fw compatibility. From 089ec16010e51ee4f5d57c154d3d306e6f564757 Mon Sep 17 00:00:00 2001 From: Sergey Kambalin Date: Tue, 13 Jun 2023 00:34:53 +0200 Subject: [PATCH 391/412] hw/arm/raspi: Import Linux raspi definitions as 'raspberrypi-fw-defs.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergey Kambalin Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-id: 20230612223456.33824-2-philmd@linaro.org Message-Id: <20230531155258.8361-1-sergey.kambalin@auriga.com> [PMD: Split from bigger patch: 1/4] Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- include/hw/misc/raspberrypi-fw-defs.h | 163 ++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 include/hw/misc/raspberrypi-fw-defs.h diff --git a/include/hw/misc/raspberrypi-fw-defs.h b/include/hw/misc/raspberrypi-fw-defs.h new file mode 100644 index 0000000000..4551fe7450 --- /dev/null +++ b/include/hw/misc/raspberrypi-fw-defs.h @@ -0,0 +1,163 @@ +/* + * Raspberry Pi firmware definitions + * + * Copyright (C) 2022 Auriga LLC, based on Linux kernel + * `include/soc/bcm2835/raspberrypi-firmware.h` (Copyright © 2015 Broadcom) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ +#define INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ + +#include "qemu/osdep.h" + +enum rpi_firmware_property_tag { + RPI_FWREQ_PROPERTY_END = 0, + RPI_FWREQ_GET_FIRMWARE_REVISION = 0x00000001, + RPI_FWREQ_GET_FIRMWARE_VARIANT = 0x00000002, + RPI_FWREQ_GET_FIRMWARE_HASH = 0x00000003, + + RPI_FWREQ_SET_CURSOR_INFO = 0x00008010, + RPI_FWREQ_SET_CURSOR_STATE = 0x00008011, + + RPI_FWREQ_GET_BOARD_MODEL = 0x00010001, + RPI_FWREQ_GET_BOARD_REVISION = 0x00010002, + RPI_FWREQ_GET_BOARD_MAC_ADDRESS = 0x00010003, + RPI_FWREQ_GET_BOARD_SERIAL = 0x00010004, + RPI_FWREQ_GET_ARM_MEMORY = 0x00010005, + RPI_FWREQ_GET_VC_MEMORY = 0x00010006, + RPI_FWREQ_GET_CLOCKS = 0x00010007, + RPI_FWREQ_GET_POWER_STATE = 0x00020001, + RPI_FWREQ_GET_TIMING = 0x00020002, + RPI_FWREQ_SET_POWER_STATE = 0x00028001, + RPI_FWREQ_GET_CLOCK_STATE = 0x00030001, + RPI_FWREQ_GET_CLOCK_RATE = 0x00030002, + RPI_FWREQ_GET_VOLTAGE = 0x00030003, + RPI_FWREQ_GET_MAX_CLOCK_RATE = 0x00030004, + RPI_FWREQ_GET_MAX_VOLTAGE = 0x00030005, + RPI_FWREQ_GET_TEMPERATURE = 0x00030006, + RPI_FWREQ_GET_MIN_CLOCK_RATE = 0x00030007, + RPI_FWREQ_GET_MIN_VOLTAGE = 0x00030008, + RPI_FWREQ_GET_TURBO = 0x00030009, + RPI_FWREQ_GET_MAX_TEMPERATURE = 0x0003000a, + RPI_FWREQ_GET_STC = 0x0003000b, + RPI_FWREQ_ALLOCATE_MEMORY = 0x0003000c, + RPI_FWREQ_LOCK_MEMORY = 0x0003000d, + RPI_FWREQ_UNLOCK_MEMORY = 0x0003000e, + RPI_FWREQ_RELEASE_MEMORY = 0x0003000f, + RPI_FWREQ_EXECUTE_CODE = 0x00030010, + RPI_FWREQ_EXECUTE_QPU = 0x00030011, + RPI_FWREQ_SET_ENABLE_QPU = 0x00030012, + RPI_FWREQ_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, + RPI_FWREQ_GET_EDID_BLOCK = 0x00030020, + RPI_FWREQ_GET_CUSTOMER_OTP = 0x00030021, + RPI_FWREQ_GET_EDID_BLOCK_DISPLAY = 0x00030023, + RPI_FWREQ_GET_DOMAIN_STATE = 0x00030030, + RPI_FWREQ_GET_THROTTLED = 0x00030046, + RPI_FWREQ_GET_CLOCK_MEASURED = 0x00030047, + RPI_FWREQ_NOTIFY_REBOOT = 0x00030048, + RPI_FWREQ_SET_CLOCK_STATE = 0x00038001, + RPI_FWREQ_SET_CLOCK_RATE = 0x00038002, + RPI_FWREQ_SET_VOLTAGE = 0x00038003, + RPI_FWREQ_SET_MAX_CLOCK_RATE = 0x00038004, + RPI_FWREQ_SET_MIN_CLOCK_RATE = 0x00038007, + RPI_FWREQ_SET_TURBO = 0x00038009, + RPI_FWREQ_SET_CUSTOMER_OTP = 0x00038021, + RPI_FWREQ_SET_DOMAIN_STATE = 0x00038030, + RPI_FWREQ_GET_GPIO_STATE = 0x00030041, + RPI_FWREQ_SET_GPIO_STATE = 0x00038041, + RPI_FWREQ_SET_SDHOST_CLOCK = 0x00038042, + RPI_FWREQ_GET_GPIO_CONFIG = 0x00030043, + RPI_FWREQ_SET_GPIO_CONFIG = 0x00038043, + RPI_FWREQ_GET_PERIPH_REG = 0x00030045, + RPI_FWREQ_SET_PERIPH_REG = 0x00038045, + RPI_FWREQ_GET_POE_HAT_VAL = 0x00030049, + RPI_FWREQ_SET_POE_HAT_VAL = 0x00038049, + RPI_FWREQ_SET_POE_HAT_VAL_OLD = 0x00030050, + RPI_FWREQ_NOTIFY_XHCI_RESET = 0x00030058, + RPI_FWREQ_GET_REBOOT_FLAGS = 0x00030064, + RPI_FWREQ_SET_REBOOT_FLAGS = 0x00038064, + RPI_FWREQ_NOTIFY_DISPLAY_DONE = 0x00030066, + + /* Dispmanx TAGS */ + RPI_FWREQ_FRAMEBUFFER_ALLOCATE = 0x00040001, + RPI_FWREQ_FRAMEBUFFER_BLANK = 0x00040002, + RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, + RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, + RPI_FWREQ_FRAMEBUFFER_GET_DEPTH = 0x00040005, + RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER = 0x00040006, + RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE = 0x00040007, + RPI_FWREQ_FRAMEBUFFER_GET_PITCH = 0x00040008, + RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009, + RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, + RPI_FWREQ_FRAMEBUFFER_GET_PALETTE = 0x0004000b, + RPI_FWREQ_FRAMEBUFFER_GET_LAYER = 0x0004000c, + RPI_FWREQ_FRAMEBUFFER_GET_TRANSFORM = 0x0004000d, + RPI_FWREQ_FRAMEBUFFER_GET_VSYNC = 0x0004000e, + RPI_FWREQ_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f, + RPI_FWREQ_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010, + RPI_FWREQ_FRAMEBUFFER_RELEASE = 0x00048001, + RPI_FWREQ_FRAMEBUFFER_GET_DISPLAY_ID = 0x00040016, + RPI_FWREQ_FRAMEBUFFER_SET_DISPLAY_NUM = 0x00048013, + RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS = 0x00040013, + RPI_FWREQ_FRAMEBUFFER_GET_DISPLAY_SETTINGS = 0x00040014, + RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, + RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH = 0x00044005, + RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER = 0x00044006, + RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE = 0x00044007, + RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009, + RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a, + RPI_FWREQ_FRAMEBUFFER_TEST_PALETTE = 0x0004400b, + RPI_FWREQ_FRAMEBUFFER_TEST_LAYER = 0x0004400c, + RPI_FWREQ_FRAMEBUFFER_TEST_TRANSFORM = 0x0004400d, + RPI_FWREQ_FRAMEBUFFER_TEST_VSYNC = 0x0004400e, + RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, + RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, + RPI_FWREQ_FRAMEBUFFER_SET_DEPTH = 0x00048005, + RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006, + RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007, + RPI_FWREQ_FRAMEBUFFER_SET_PITCH = 0x00048008, + RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009, + RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, + RPI_FWREQ_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + + RPI_FWREQ_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f, + RPI_FWREQ_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020, + RPI_FWREQ_FRAMEBUFFER_SET_VSYNC = 0x0004800e, + RPI_FWREQ_FRAMEBUFFER_SET_LAYER = 0x0004800c, + RPI_FWREQ_FRAMEBUFFER_SET_TRANSFORM = 0x0004800d, + RPI_FWREQ_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f, + + RPI_FWREQ_VCHIQ_INIT = 0x00048010, + + RPI_FWREQ_SET_PLANE = 0x00048015, + RPI_FWREQ_GET_DISPLAY_TIMING = 0x00040017, + RPI_FWREQ_SET_TIMING = 0x00048017, + RPI_FWREQ_GET_DISPLAY_CFG = 0x00040018, + RPI_FWREQ_SET_DISPLAY_POWER = 0x00048019, + RPI_FWREQ_GET_COMMAND_LINE = 0x00050001, + RPI_FWREQ_GET_DMA_CHANNELS = 0x00060001, +}; + +enum rpi_firmware_clk_id { + RPI_FIRMWARE_EMMC_CLK_ID = 1, + RPI_FIRMWARE_UART_CLK_ID, + RPI_FIRMWARE_ARM_CLK_ID, + RPI_FIRMWARE_CORE_CLK_ID, + RPI_FIRMWARE_V3D_CLK_ID, + RPI_FIRMWARE_H264_CLK_ID, + RPI_FIRMWARE_ISP_CLK_ID, + RPI_FIRMWARE_SDRAM_CLK_ID, + RPI_FIRMWARE_PIXEL_CLK_ID, + RPI_FIRMWARE_PWM_CLK_ID, + RPI_FIRMWARE_HEVC_CLK_ID, + RPI_FIRMWARE_EMMC2_CLK_ID, + RPI_FIRMWARE_M2MC_CLK_ID, + RPI_FIRMWARE_PIXEL_BVB_CLK_ID, + RPI_FIRMWARE_VEC_CLK_ID, + RPI_FIRMWARE_NUM_CLK_ID, +}; + +#endif /* INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ */ From 2519182666666a8c08bbcdb053464459e93a2451 Mon Sep 17 00:00:00 2001 From: Sergey Kambalin Date: Tue, 13 Jun 2023 00:34:54 +0200 Subject: [PATCH 392/412] hw/misc/bcm2835_property: Use 'raspberrypi-fw-defs.h' definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace magic property values by a proper definition, removing redundant comments. Signed-off-by: Sergey Kambalin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20230612223456.33824-3-philmd@linaro.org Message-Id: <20230531155258.8361-1-sergey.kambalin@auriga.com> [PMD: Split from bigger patch: 2/4] Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/misc/bcm2835_property.c | 101 +++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 251b3d865d..7d398a6f75 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -12,6 +12,7 @@ #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/misc/bcm2835_mbox_defs.h" +#include "hw/misc/raspberrypi-fw-defs.h" #include "sysemu/dma.h" #include "qemu/log.h" #include "qemu/module.h" @@ -51,48 +52,48 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* @(value + 8) : Request/response indicator */ resplen = 0; switch (tag) { - case 0x00000000: /* End tag */ + case RPI_FWREQ_PROPERTY_END: break; - case 0x00000001: /* Get firmware revision */ + case RPI_FWREQ_GET_FIRMWARE_REVISION: stl_le_phys(&s->dma_as, value + 12, 346337); resplen = 4; break; - case 0x00010001: /* Get board model */ + case RPI_FWREQ_GET_BOARD_MODEL: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x get board model NYI\n", tag); resplen = 4; break; - case 0x00010002: /* Get board revision */ + case RPI_FWREQ_GET_BOARD_REVISION: stl_le_phys(&s->dma_as, value + 12, s->board_rev); resplen = 4; break; - case 0x00010003: /* Get board MAC address */ + case RPI_FWREQ_GET_BOARD_MAC_ADDRESS: resplen = sizeof(s->macaddr.a); dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen, MEMTXATTRS_UNSPECIFIED); break; - case 0x00010004: /* Get board serial */ + case RPI_FWREQ_GET_BOARD_SERIAL: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x get board serial NYI\n", tag); resplen = 8; break; - case 0x00010005: /* Get ARM memory */ + case RPI_FWREQ_GET_ARM_MEMORY: /* base */ stl_le_phys(&s->dma_as, value + 12, 0); /* size */ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); resplen = 8; break; - case 0x00010006: /* Get VC memory */ + case RPI_FWREQ_GET_VC_MEMORY: /* base */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); /* size */ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); resplen = 8; break; - case 0x00028001: /* Set power state */ + case RPI_FWREQ_SET_POWER_STATE: /* Assume that whatever device they asked for exists, * and we'll just claim we set it to the desired state */ @@ -103,26 +104,26 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Clocks */ - case 0x00030001: /* Get clock state */ + case RPI_FWREQ_GET_CLOCK_STATE: stl_le_phys(&s->dma_as, value + 16, 0x1); resplen = 8; break; - case 0x00038001: /* Set clock state */ + case RPI_FWREQ_SET_CLOCK_STATE: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x set clock state NYI\n", tag); resplen = 8; break; - case 0x00030002: /* Get clock rate */ - case 0x00030004: /* Get max clock rate */ - case 0x00030007: /* Get min clock rate */ + case RPI_FWREQ_GET_CLOCK_RATE: + case RPI_FWREQ_GET_MAX_CLOCK_RATE: + case RPI_FWREQ_GET_MIN_CLOCK_RATE: switch (ldl_le_phys(&s->dma_as, value + 12)) { - case 1: /* EMMC */ + case RPI_FIRMWARE_EMMC_CLK_ID: stl_le_phys(&s->dma_as, value + 16, 50000000); break; - case 2: /* UART */ + case RPI_FIRMWARE_UART_CLK_ID: stl_le_phys(&s->dma_as, value + 16, 3000000); break; default: @@ -132,9 +133,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; - case 0x00038002: /* Set clock rate */ - case 0x00038004: /* Set max clock rate */ - case 0x00038007: /* Set min clock rate */ + case RPI_FWREQ_SET_CLOCK_RATE: + case RPI_FWREQ_SET_MAX_CLOCK_RATE: + case RPI_FWREQ_SET_MIN_CLOCK_RATE: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x set clock rate NYI\n", tag); @@ -143,121 +144,121 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Temperature */ - case 0x00030006: /* Get temperature */ + case RPI_FWREQ_GET_TEMPERATURE: stl_le_phys(&s->dma_as, value + 16, 25000); resplen = 8; break; - case 0x0003000A: /* Get max temperature */ + case RPI_FWREQ_GET_MAX_TEMPERATURE: stl_le_phys(&s->dma_as, value + 16, 99000); resplen = 8; break; /* Frame buffer */ - case 0x00040001: /* Allocate buffer */ + case RPI_FWREQ_FRAMEBUFFER_ALLOCATE: stl_le_phys(&s->dma_as, value + 12, fbconfig.base); stl_le_phys(&s->dma_as, value + 16, bcm2835_fb_get_size(&fbconfig)); resplen = 8; break; - case 0x00048001: /* Release buffer */ + case RPI_FWREQ_FRAMEBUFFER_RELEASE: resplen = 0; break; - case 0x00040002: /* Blank screen */ + case RPI_FWREQ_FRAMEBUFFER_BLANK: resplen = 4; break; - case 0x00044003: /* Test physical display width/height */ - case 0x00044004: /* Test virtual display width/height */ + case RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT: + case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT: resplen = 8; break; - case 0x00048003: /* Set physical display width/height */ + case RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT: fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040003: /* Get physical display width/height */ + case RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT: stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); resplen = 8; break; - case 0x00048004: /* Set virtual display width/height */ + case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT: fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040004: /* Get virtual display width/height */ + case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT: stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); resplen = 8; break; - case 0x00044005: /* Test depth */ + case RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH: resplen = 4; break; - case 0x00048005: /* Set depth */ + case RPI_FWREQ_FRAMEBUFFER_SET_DEPTH: fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040005: /* Get depth */ + case RPI_FWREQ_FRAMEBUFFER_GET_DEPTH: stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); resplen = 4; break; - case 0x00044006: /* Test pixel order */ + case RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER: resplen = 4; break; - case 0x00048006: /* Set pixel order */ + case RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER: fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040006: /* Get pixel order */ + case RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER: stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); resplen = 4; break; - case 0x00044007: /* Test pixel alpha */ + case RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE: resplen = 4; break; - case 0x00048007: /* Set alpha */ + case RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE: fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040007: /* Get alpha */ + case RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE: stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); resplen = 4; break; - case 0x00040008: /* Get pitch */ + case RPI_FWREQ_FRAMEBUFFER_GET_PITCH: stl_le_phys(&s->dma_as, value + 12, bcm2835_fb_get_pitch(&fbconfig)); resplen = 4; break; - case 0x00044009: /* Test virtual offset */ + case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET: resplen = 8; break; - case 0x00048009: /* Set virtual offset */ + case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET: fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040009: /* Get virtual offset */ + case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET: stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); resplen = 8; break; - case 0x0004000a: /* Get/Test/Set overscan */ - case 0x0004400a: - case 0x0004800a: + case RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN: + case RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN: + case RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN: stl_le_phys(&s->dma_as, value + 12, 0); stl_le_phys(&s->dma_as, value + 16, 0); stl_le_phys(&s->dma_as, value + 20, 0); stl_le_phys(&s->dma_as, value + 24, 0); resplen = 16; break; - case 0x0004800b: /* Set palette */ + case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: offset = ldl_le_phys(&s->dma_as, value + 12); length = ldl_le_phys(&s->dma_as, value + 16); n = 0; @@ -270,18 +271,18 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) stl_le_phys(&s->dma_as, value + 12, 0); resplen = 4; break; - case 0x00040013: /* Get number of displays */ + case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: stl_le_phys(&s->dma_as, value + 12, 1); resplen = 4; break; - case 0x00060001: /* Get DMA channels */ + case RPI_FWREQ_GET_DMA_CHANNELS: /* channels 2-5 */ stl_le_phys(&s->dma_as, value + 12, 0x003C); resplen = 4; break; - case 0x00050001: /* Get command line */ + case RPI_FWREQ_GET_COMMAND_LINE: /* * We follow the firmware behaviour: no NUL terminator is * written to the buffer, and if the buffer is too short From 5dc496363a5bd7bec9cf0287cfbece9b94bcfa9a Mon Sep 17 00:00:00 2001 From: Sergey Kambalin Date: Tue, 13 Jun 2023 00:34:55 +0200 Subject: [PATCH 393/412] hw/misc/bcm2835_property: Replace magic frequency values by definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergey Kambalin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20230612223456.33824-4-philmd@linaro.org Message-Id: <20230531155258.8361-1-sergey.kambalin@auriga.com> [PMD: Split from bigger patch: 4/4] Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/misc/bcm2835_property.c | 8 +++++--- include/hw/arm/raspi_platform.h | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 7d398a6f75..2e4fe969bf 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -17,6 +17,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" +#include "hw/arm/raspi_platform.h" /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ @@ -121,13 +122,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case RPI_FWREQ_GET_MIN_CLOCK_RATE: switch (ldl_le_phys(&s->dma_as, value + 12)) { case RPI_FIRMWARE_EMMC_CLK_ID: - stl_le_phys(&s->dma_as, value + 16, 50000000); + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_EMMC_CLK_RATE); break; case RPI_FIRMWARE_UART_CLK_ID: - stl_le_phys(&s->dma_as, value + 16, 3000000); + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE); break; default: - stl_le_phys(&s->dma_as, value + 16, 700000000); + stl_le_phys(&s->dma_as, value + 16, + RPI_FIRMWARE_DEFAULT_CLK_RATE); break; } resplen = 8; diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h index 4a56dd4b89..83f2588fc5 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -170,4 +170,9 @@ #define INTERRUPT_ILLEGAL_TYPE0 6 #define INTERRUPT_ILLEGAL_TYPE1 7 +/* Clock rates */ +#define RPI_FIRMWARE_EMMC_CLK_RATE 50000000 +#define RPI_FIRMWARE_UART_CLK_RATE 3000000 +#define RPI_FIRMWARE_DEFAULT_CLK_RATE 700000000 + #endif From 074259c0f2ac40042dce766d870318cc22f388eb Mon Sep 17 00:00:00 2001 From: Sergey Kambalin Date: Tue, 13 Jun 2023 00:34:56 +0200 Subject: [PATCH 394/412] hw/misc/bcm2835_property: Handle CORE_CLK_ID firmware property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergey Kambalin Signed-off-by: Philippe Mathieu-Daudé Message-id: 20230612223456.33824-5-philmd@linaro.org Message-Id: <20230531155258.8361-1-sergey.kambalin@auriga.com> [PMD: Split from bigger patch: 3/4] Signed-off-by: Philippe Mathieu-Daudé [PMM: added a comment about RPI_FIRMWARE_CORE_CLK_RATE really being SoC-specific] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/bcm2835_property.c | 3 +++ include/hw/arm/raspi_platform.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 2e4fe969bf..4ed9faa54a 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -127,6 +127,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case RPI_FIRMWARE_UART_CLK_ID: stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE); break; + case RPI_FIRMWARE_CORE_CLK_ID: + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_CORE_CLK_RATE); + break; default: stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_DEFAULT_CLK_RATE); diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h index 83f2588fc5..ede98e63c3 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -173,6 +173,11 @@ /* Clock rates */ #define RPI_FIRMWARE_EMMC_CLK_RATE 50000000 #define RPI_FIRMWARE_UART_CLK_RATE 3000000 +/* + * TODO: this is really SoC-specific; we might want to + * set it per-SoC if it turns out any guests care. + */ +#define RPI_FIRMWARE_CORE_CLK_RATE 350000000 #define RPI_FIRMWARE_DEFAULT_CLK_RATE 700000000 #endif From 8a2bd747877ac269d3ae06dc14029f479582acfd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 9 Jun 2023 10:58:20 -0700 Subject: [PATCH 395/412] host/include/x86_64: Use __m128i for "x" constraints The macOS catalina compiler produces an error for __int128_t as the type for allocation with SSE inline asm constraint. Create a new X86Int128Union type and use the vector type for all SSE register inputs and outputs. Tested-by: Peter Maydell Signed-off-by: Richard Henderson --- host/include/x86_64/host/atomic128-ldst.h | 25 ++++++++++++------- .../x86_64/host/load-extract-al16-al8.h | 8 +++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/host/include/x86_64/host/atomic128-ldst.h b/host/include/x86_64/host/atomic128-ldst.h index adc9332f91..8d6f909d3c 100644 --- a/host/include/x86_64/host/atomic128-ldst.h +++ b/host/include/x86_64/host/atomic128-ldst.h @@ -8,12 +8,19 @@ * atomic primitive is meant to provide. */ -#ifndef AARCH64_ATOMIC128_LDST_H -#define AARCH64_ATOMIC128_LDST_H +#ifndef X86_64_ATOMIC128_LDST_H +#define X86_64_ATOMIC128_LDST_H #ifdef CONFIG_INT128_TYPE #include "host/cpuinfo.h" #include "tcg/debug-assert.h" +#include + +typedef union { + __m128i v; + __int128_t i; + Int128 s; +} X86Int128Union; /* * Through clang 16, with -mcx16, __atomic_load_n is incorrectly @@ -25,10 +32,10 @@ static inline Int128 atomic16_read_ro(const Int128 *ptr) { - Int128Alias r; + X86Int128Union r; tcg_debug_assert(HAVE_ATOMIC128_RO); - asm("vmovdqa %1, %0" : "=x" (r.i) : "m" (*ptr)); + asm("vmovdqa %1, %0" : "=x" (r.v) : "m" (*ptr)); return r.s; } @@ -36,10 +43,10 @@ static inline Int128 atomic16_read_ro(const Int128 *ptr) static inline Int128 atomic16_read_rw(Int128 *ptr) { __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); - Int128Alias r; + X86Int128Union r; if (HAVE_ATOMIC128_RO) { - asm("vmovdqa %1, %0" : "=x" (r.i) : "m" (*ptr_align)); + asm("vmovdqa %1, %0" : "=x" (r.v) : "m" (*ptr_align)); } else { r.i = __sync_val_compare_and_swap_16(ptr_align, 0, 0); } @@ -49,10 +56,10 @@ static inline Int128 atomic16_read_rw(Int128 *ptr) static inline void atomic16_set(Int128 *ptr, Int128 val) { __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); - Int128Alias new = { .s = val }; + X86Int128Union new = { .s = val }; if (HAVE_ATOMIC128_RO) { - asm("vmovdqa %1, %0" : "=m"(*ptr_align) : "x" (new.i)); + asm("vmovdqa %1, %0" : "=m"(*ptr_align) : "x" (new.v)); } else { __int128_t old; do { @@ -65,4 +72,4 @@ static inline void atomic16_set(Int128 *ptr, Int128 val) #include "host/include/generic/host/atomic128-ldst.h" #endif -#endif /* AARCH64_ATOMIC128_LDST_H */ +#endif /* X86_64_ATOMIC128_LDST_H */ diff --git a/host/include/x86_64/host/load-extract-al16-al8.h b/host/include/x86_64/host/load-extract-al16-al8.h index 31b6fe8c45..baa506b7b5 100644 --- a/host/include/x86_64/host/load-extract-al16-al8.h +++ b/host/include/x86_64/host/load-extract-al16-al8.h @@ -9,7 +9,7 @@ #define X86_64_LOAD_EXTRACT_AL16_AL8_H #ifdef CONFIG_INT128_TYPE -#include "host/cpuinfo.h" +#include "host/atomic128-ldst.h" /** * load_atom_extract_al16_or_al8: @@ -26,7 +26,7 @@ load_atom_extract_al16_or_al8(void *pv, int s) uintptr_t pi = (uintptr_t)pv; __int128_t *ptr_align = (__int128_t *)(pi & ~7); int shr = (pi & 7) * 8; - Int128Alias r; + X86Int128Union r; /* * ptr_align % 16 is now only 0 or 8. @@ -35,9 +35,9 @@ load_atom_extract_al16_or_al8(void *pv, int s) * when ptr_align % 16 == 0 for 16-byte atomicity. */ if ((cpuinfo & CPUINFO_ATOMIC_VMOVDQU) || (pi & 8)) { - asm("vmovdqu %1, %0" : "=x" (r.i) : "m" (*ptr_align)); + asm("vmovdqu %1, %0" : "=x" (r.v) : "m" (*ptr_align)); } else { - asm("vmovdqa %1, %0" : "=x" (r.i) : "m" (*ptr_align)); + asm("vmovdqa %1, %0" : "=x" (r.v) : "m" (*ptr_align)); } return int128_getlo(int128_urshift(r.s, shr)); } From 2be6a48673afb3a15ad7a611d5562256707fdc46 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 19 Jun 2023 15:23:14 +0200 Subject: [PATCH 396/412] accel/tcg: Handle MO_ATOM_WITHIN16 in do_st16_leN Otherwise we hit the default assert not reached. Handle it as MO_ATOM_NONE, because of size and misalignment. We already handle this correctly in do_ld16_beN. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 5e2ca47243..14ce97c33b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2727,6 +2727,7 @@ static uint64_t do_st16_leN(CPUArchState *env, MMULookupPageData *p, * and so neither is atomic. */ case MO_ATOM_IFALIGN: + case MO_ATOM_WITHIN16: case MO_ATOM_NONE: stq_le_p(p->haddr, int128_getlo(val_le)); return store_bytes_leN(p->haddr + 8, p->size - 8, From dc8a8cc879416620fba7ddff4cb9e766d6873f1b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jun 2023 00:40:31 +0300 Subject: [PATCH 397/412] tcg/ppc: Define _CALL_AIX for clang on ppc64(be) Restructure the ifdef ladder, separating 64-bit from 32-bit, and ensure _CALL_AIX is set for ELF v1. Fixes the build for ppc64 big-endian host with clang. Reviewed-by: Daniel Henrique Barboza Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target.c.inc | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 507fe6cda8..5c8378f8f6 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -29,15 +29,24 @@ /* * Standardize on the _CALL_FOO symbols used by GCC: * Apple XCode does not define _CALL_DARWIN. - * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV (32-bit). + * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV or _CALL_AIX. */ -#if !defined(_CALL_SYSV) && \ - !defined(_CALL_DARWIN) && \ - !defined(_CALL_AIX) && \ - !defined(_CALL_ELF) -# if defined(__APPLE__) +#if TCG_TARGET_REG_BITS == 64 +# ifdef _CALL_AIX + /* ok */ +# elif defined(_CALL_ELF) && _CALL_ELF == 1 +# define _CALL_AIX +# elif defined(_CALL_ELF) && _CALL_ELF == 2 + /* ok */ +# else +# error "Unknown ABI" +# endif +#else +# if defined(_CALL_SYSV) || defined(_CALL_DARWIN) + /* ok */ +# elif defined(__APPLE__) # define _CALL_DARWIN -# elif defined(__ELF__) && TCG_TARGET_REG_BITS == 32 +# elif defined(__ELF__) # define _CALL_SYSV # else # error "Unknown ABI" From 1dc7bb0e96b127511862ab0e088946f8648ac7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:39 +0200 Subject: [PATCH 398/412] target/i386: Simplify i386_tr_init_disas_context() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since cpu_mmu_index() is well-defined for user-only, we can remove the surrounding #ifdef'ry entirely. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-2-philmd@linaro.org> Signed-off-by: Richard Henderson --- target/i386/tcg/translate.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5cf14311a6..08c4cab73f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -6914,10 +6914,7 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cc_op_dirty = false; dc->popl_esp_hack = 0; /* select memory access functions */ - dc->mem_index = 0; -#ifdef CONFIG_SOFTMMU dc->mem_index = cpu_mmu_index(env, false); -#endif dc->cpuid_features = env->features[FEAT_1_EDX]; dc->cpuid_ext_features = env->features[FEAT_1_ECX]; dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; From 66352d7ef2e304047006ee1e2dab08d01518f1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:40 +0200 Subject: [PATCH 399/412] target/tricore: Remove pointless CONFIG_SOFTMMU guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't build any user emulation target for Tricore, only the system emulation. No need to check for it as it is always defined. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Bastian Koppelmann Message-Id: <20230613133347.82210-3-philmd@linaro.org> Signed-off-by: Richard Henderson --- target/tricore/helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 284a749e50..951024d491 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -31,7 +31,6 @@ enum { TLBRET_MATCH = 0 }; -#if defined(CONFIG_SOFTMMU) static int get_physical_address(CPUTriCoreState *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx) @@ -57,7 +56,6 @@ hwaddr tricore_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } return phys_addr; } -#endif /* TODO: Add exeption support*/ static void raise_mmu_exception(CPUTriCoreState *env, target_ulong address, From 6a14058677dd2af838f0b2daa9896ce569259a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:41 +0200 Subject: [PATCH 400/412] target/m68k: Check for USER_ONLY definition instead of SOFTMMU one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we *might* have user emulation with softmmu, replace the system emulation check by !user emulation one. Invert some if() ladders for clarity. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-4-philmd@linaro.org> Signed-off-by: Richard Henderson --- target/m68k/cpu.c | 14 ++++++-------- target/m68k/helper.c | 4 ++-- target/m68k/helper.h | 2 +- target/m68k/translate.c | 28 ++++++++++++++-------------- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 99af1ab541..70d58471dc 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -80,10 +80,10 @@ static void m68k_cpu_reset_hold(Object *obj) } memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); -#ifdef CONFIG_SOFTMMU - cpu_m68k_set_sr(env, SR_S | SR_I); -#else +#ifdef CONFIG_USER_ONLY cpu_m68k_set_sr(env, 0); +#else + cpu_m68k_set_sr(env, SR_S | SR_I); #endif for (i = 0; i < 8; i++) { env->fregs[i].d = nan; @@ -334,7 +334,7 @@ static void m68k_cpu_initfn(Object *obj) cpu_set_cpustate_pointers(cpu); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) static bool fpu_needed(void *opaque) { M68kCPU *s = opaque; @@ -525,15 +525,13 @@ static const VMStateDescription vmstate_m68k_cpu = { NULL }, }; -#endif -#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps m68k_sysemu_ops = { .get_phys_page_debug = m68k_cpu_get_phys_page_debug, }; -#endif +#endif /* !CONFIG_USER_ONLY */ #include "hw/core/tcg-cpu-ops.h" @@ -568,7 +566,7 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) cc->get_pc = m68k_cpu_get_pc; cc->gdb_read_register = m68k_cpu_gdb_read_register; cc->gdb_write_register = m68k_cpu_gdb_write_register; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) dc->vmsd = &vmstate_m68k_cpu; cc->sysemu_ops = &m68k_sysemu_ops; #endif diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 3b3a6ea8bd..01c18a7c59 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -1480,7 +1480,7 @@ void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc) env->macc[acc + 1] = res; } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) void HELPER(ptest)(CPUM68KState *env, uint32_t addr, uint32_t is_read) { hwaddr physical; @@ -1534,4 +1534,4 @@ void HELPER(reset)(CPUM68KState *env) { /* FIXME: reset all except CPU */ } -#endif +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/m68k/helper.h b/target/m68k/helper.h index c9bed2b884..2bbe0dc032 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -124,7 +124,7 @@ DEF_HELPER_FLAGS_4(bfffo_mem, TCG_CALL_NO_WG, i64, env, i32, s32, i32) DEF_HELPER_3(chk, void, env, s32, s32) DEF_HELPER_4(chk2, void, env, s32, s32, s32) -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DEF_HELPER_3(ptest, void, env, i32, i32) DEF_HELPER_3(pflush, void, env, i32, i32) DEF_HELPER_FLAGS_1(reset, TCG_CALL_NO_RWG, void, env) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 551ef9e52a..e07161d76f 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -2637,10 +2637,10 @@ DISAS_INSN(swap) DISAS_INSN(bkpt) { -#if defined(CONFIG_SOFTMMU) - gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); -#else +#if defined(CONFIG_USER_ONLY) gen_exception(s, s->base.pc_next, EXCP_DEBUG); +#else + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); #endif } @@ -2838,7 +2838,7 @@ DISAS_INSN(unlk) tcg_gen_addi_i32(QREG_SP, src, 4); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(reset) { if (IS_USER(s)) { @@ -4398,7 +4398,7 @@ DISAS_INSN(move_from_sr) DEST_EA(env, insn, OS_WORD, sr, NULL); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(moves) { int opsize; @@ -4605,7 +4605,7 @@ DISAS_INSN(cinv) /* Invalidate cache line. Implement as no-op. */ } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(pflush) { TCGv opmode; @@ -5352,7 +5352,7 @@ DISAS_INSN(ftrapcc) do_trapcc(s, &c); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(frestore) { TCGv addr; @@ -5795,7 +5795,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(bitop_im, 08c0, ffc0); INSN(arith_im, 0a80, fff8, CF_ISA_A); INSN(arith_im, 0a00, ff00, M68K); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(moves, 0e00, ff00, M68K); #endif INSN(cas, 0ac0, ffc0, CAS); @@ -5824,7 +5824,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(move_to_ccr, 44c0, ffc0); INSN(not, 4680, fff8, CF_ISA_A); INSN(not, 4600, ff00, M68K); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) BASE(move_to_sr, 46c0, ffc0); #endif INSN(nbcd, 4800, ffc0, M68K); @@ -5841,7 +5841,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(tst, 4a00, ff00); INSN(tas, 4ac0, ffc0, CF_ISA_B); INSN(tas, 4ac0, ffc0, M68K); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(halt, 4ac8, ffff, CF_ISA_A); INSN(halt, 4ac8, ffff, M68K); #endif @@ -5855,7 +5855,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(trap, 4e40, fff0); BASE(link, 4e50, fff8); BASE(unlk, 4e58, fff8); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(move_to_usp, 4e60, fff8, USP); INSN(move_from_usp, 4e68, fff8, USP); INSN(reset, 4e70, ffff, M68K); @@ -5980,7 +5980,7 @@ void register_m68k_insns (CPUM68KState *env) INSN(ftrapcc, f27a, fffe, FPU); /* opmode 010, 011 */ INSN(ftrapcc, f27c, ffff, FPU); /* opmode 100 */ INSN(fbcc, f280, ff80, FPU); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(frestore, f340, ffc0, CF_FPU); INSN(fsave, f300, ffc0, CF_FPU); INSN(frestore, f340, ffc0, FPU); @@ -6190,7 +6190,7 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, int flags) break; } qemu_fprintf(f, "\n"); -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY qemu_fprintf(f, "%sA7(MSP) = %08x %sA7(USP) = %08x %sA7(ISP) = %08x\n", env->current_sp == M68K_SSP ? "->" : " ", env->sp[M68K_SSP], env->current_sp == M68K_USP ? "->" : " ", env->sp[M68K_USP], @@ -6204,5 +6204,5 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, int flags) env->mmu.ttr[M68K_ITTR0], env->mmu.ttr[M68K_ITTR1]); qemu_fprintf(f, "MMUSR %08x, fault at %08x\n", env->mmu.mmusr, env->mmu.ar); -#endif +#endif /* !CONFIG_USER_ONLY */ } From 227776b7e2768f9912cfcb6def4c740471cd2170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:42 +0200 Subject: [PATCH 401/412] target/ppc: Check for USER_ONLY definition instead of SOFTMMU one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we *might* have user emulation with softmmu, replace the system emulation check by !user emulation one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Nicholas Piggin Message-Id: <20230613133347.82210-5-philmd@linaro.org> Signed-off-by: Richard Henderson --- target/ppc/cpu_init.c | 20 ++++++++++---------- target/ppc/helper_regs.c | 6 ++---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9f97222655..7bce421a7c 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5841,7 +5841,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI); pcc->mmu_model = POWERPC_MMU_64B; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; @@ -5920,7 +5920,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->lpcr_mask = LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 | LPCR_RMI | LPCR_HDICE; pcc->mmu_model = POWERPC_MMU_2_03; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_basic; pcc->lrg_decr_bits = 32; #endif @@ -6037,7 +6037,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE; pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; pcc->mmu_model = POWERPC_MMU_2_06; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->lrg_decr_bits = 32; #endif @@ -6181,7 +6181,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4; pcc->mmu_model = POWERPC_MMU_2_07; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->lrg_decr_bits = 32; pcc->n_host_threads = 8; @@ -6197,7 +6197,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY /* * Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings * Encoded as array of int_32s in the form: @@ -6214,7 +6214,7 @@ static struct ppc_radix_page_info POWER9_radix_page_info = { 0x4000001e /* 1G - enc: 0x2 */ } }; -#endif /* CONFIG_SOFTMMU */ +#endif /* CONFIG_USER_ONLY */ static void init_proc_POWER9(CPUPPCState *env) { @@ -6371,7 +6371,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER9_radix_page_info; @@ -6389,7 +6389,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY /* * Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings * Encoded as array of int_32s in the form: @@ -6406,7 +6406,7 @@ static struct ppc_radix_page_info POWER10_radix_page_info = { 0x4000001e /* 1G - enc: 0x2 */ } }; -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ static void init_proc_POWER10(CPUPPCState *env) { @@ -6547,7 +6547,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER10_radix_page_info; diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index bc7e9d7eda..e27f4a75a4 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -310,7 +310,7 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) return excp; } -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY void store_40x_sler(CPUPPCState *env, uint32_t val) { /* XXX: TO BE FIXED */ @@ -320,9 +320,7 @@ void store_40x_sler(CPUPPCState *env, uint32_t val) } env->spr[SPR_405_SLER] = val; } -#endif /* CONFIG_SOFTMMU */ -#ifndef CONFIG_USER_ONLY void check_tlb_flush(CPUPPCState *env, bool global) { CPUState *cs = env_cpu(env); @@ -341,7 +339,7 @@ void check_tlb_flush(CPUPPCState *env, bool global) tlb_flush(cs); } } -#endif +#endif /* !CONFIG_USER_ONLY */ /** * _spr_register From 75fe97b429d491d348fde78ec75c6556762b4b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:43 +0200 Subject: [PATCH 402/412] hw/core/cpu: Check for USER_ONLY definition instead of SOFTMMU one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we *might* have user emulation with softmmu, replace the system emulation check by !user emulation one. Invert the #ifdef'ry in TCGCPUOps structure for clarity. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-6-philmd@linaro.org> Signed-off-by: Richard Henderson --- include/hw/core/cpu.h | 4 +- include/hw/core/tcg-cpu-ops.h | 102 +++++++++++++++++----------------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index d84fbccaab..4871ad85f0 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1016,7 +1016,7 @@ void page_size_init(void); #ifdef NEED_CPU_H -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_cpu_common; @@ -1027,7 +1027,7 @@ extern const VMStateDescription vmstate_cpu_common; .flags = VMS_STRUCT, \ .offset = 0, \ } -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ #endif /* NEED_CPU_H */ diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 0ae08df47e..3e8b1b737a 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -64,7 +64,56 @@ struct TCGCPUOps { */ void (*do_interrupt)(CPUState *cpu); #endif /* !CONFIG_USER_ONLY || !TARGET_I386 */ -#ifdef CONFIG_SOFTMMU +#ifdef CONFIG_USER_ONLY + /** + * record_sigsegv: + * @cpu: cpu context + * @addr: faulting guest address + * @access_type: access was read/write/execute + * @maperr: true for invalid page, false for permission fault + * @ra: host pc for unwinding + * + * We are about to raise SIGSEGV with si_code set for @maperr, + * and si_addr set for @addr. Record anything further needed + * for the signal ucontext_t. + * + * If the emulated kernel does not provide anything to the signal + * handler with anything besides the user context registers, and + * the siginfo_t, then this hook need do nothing and may be omitted. + * Otherwise, record the data and return; the caller will raise + * the signal, unwind the cpu state, and return to the main loop. + * + * If it is simpler to re-use the sysemu tlb_fill code, @ra is provided + * so that a "normal" cpu exception can be raised. In this case, + * the signal must be raised by the architecture cpu_loop. + */ + void (*record_sigsegv)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); + /** + * record_sigbus: + * @cpu: cpu context + * @addr: misaligned guest address + * @access_type: access was read/write/execute + * @ra: host pc for unwinding + * + * We are about to raise SIGBUS with si_code BUS_ADRALN, + * and si_addr set for @addr. Record anything further needed + * for the signal ucontext_t. + * + * If the emulated kernel does not provide the signal handler with + * anything besides the user context registers, and the siginfo_t, + * then this hook need do nothing and may be omitted. + * Otherwise, record the data and return; the caller will raise + * the signal, unwind the cpu state, and return to the main loop. + * + * If it is simpler to re-use the sysemu do_unaligned_access code, + * @ra is provided so that a "normal" cpu exception can be raised. + * In this case, the signal must be raised by the architecture cpu_loop. + */ + void (*record_sigbus)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, uintptr_t ra); +#else /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); /** @@ -121,56 +170,7 @@ struct TCGCPUOps { */ bool (*io_recompile_replay_branch)(CPUState *cpu, const TranslationBlock *tb); -#else - /** - * record_sigsegv: - * @cpu: cpu context - * @addr: faulting guest address - * @access_type: access was read/write/execute - * @maperr: true for invalid page, false for permission fault - * @ra: host pc for unwinding - * - * We are about to raise SIGSEGV with si_code set for @maperr, - * and si_addr set for @addr. Record anything further needed - * for the signal ucontext_t. - * - * If the emulated kernel does not provide anything to the signal - * handler with anything besides the user context registers, and - * the siginfo_t, then this hook need do nothing and may be omitted. - * Otherwise, record the data and return; the caller will raise - * the signal, unwind the cpu state, and return to the main loop. - * - * If it is simpler to re-use the sysemu tlb_fill code, @ra is provided - * so that a "normal" cpu exception can be raised. In this case, - * the signal must be raised by the architecture cpu_loop. - */ - void (*record_sigsegv)(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); - /** - * record_sigbus: - * @cpu: cpu context - * @addr: misaligned guest address - * @access_type: access was read/write/execute - * @ra: host pc for unwinding - * - * We are about to raise SIGBUS with si_code BUS_ADRALN, - * and si_addr set for @addr. Record anything further needed - * for the signal ucontext_t. - * - * If the emulated kernel does not provide the signal handler with - * anything besides the user context registers, and the siginfo_t, - * then this hook need do nothing and may be omitted. - * Otherwise, record the data and return; the caller will raise - * the signal, unwind the cpu state, and return to the main loop. - * - * If it is simpler to re-use the sysemu do_unaligned_access code, - * @ra is provided so that a "normal" cpu exception can be raised. - * In this case, the signal must be raised by the architecture cpu_loop. - */ - void (*record_sigbus)(CPUState *cpu, vaddr addr, - MMUAccessType access_type, uintptr_t ra); -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ #endif /* NEED_CPU_H */ }; From 905db98a73d6a6e1dda2bf81ed11323efee0f7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:44 +0200 Subject: [PATCH 403/412] accel/tcg: Check for USER_ONLY definition instead of SOFTMMU one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we *might* have user emulation with softmmu, replace the system emulation check by !user emulation one. Invert some if() ladders for clarity. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-7-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 4 ++-- accel/tcg/internal.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ebc1db03d7..c0ab00385f 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -566,7 +566,7 @@ void cpu_exec_step_atomic(CPUState *cpu) cpu_tb_exec(cpu, tb, &tb_exit); cpu_exec_exit(cpu); } else { -#ifndef CONFIG_SOFTMMU +#ifdef CONFIG_USER_ONLY clear_helper_retaddr(); if (have_mmap_lock()) { mmap_unlock(); @@ -1023,7 +1023,7 @@ static int cpu_exec_setjmp(CPUState *cpu, SyncClocks *sc) /* Non-buggy compilers preserve this; assert the correct value. */ g_assert(cpu == current_cpu); -#ifndef CONFIG_SOFTMMU +#ifdef CONFIG_USER_ONLY clear_helper_retaddr(); if (have_mmap_lock()) { mmap_unlock(); diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h index 24f225cac7..65380ccb42 100644 --- a/accel/tcg/internal.h +++ b/accel/tcg/internal.h @@ -17,10 +17,10 @@ * memory related structures are protected with mmap_lock. * In !user-mode we use per-page locks. */ -#ifdef CONFIG_SOFTMMU -#define assert_memory_lock() -#else +#ifdef CONFIG_USER_ONLY #define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) +#else +#define assert_memory_lock() #endif #if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG) From bd0c03b2d3af12a410596b878c6c403110ec9db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:45 +0200 Subject: [PATCH 404/412] meson: Alias CONFIG_SOFTMMU -> CONFIG_SYSTEM_ONLY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use the CONFIG_USER_ONLY key to describe user emulation, and the CONFIG_SOFTMMU key to describe system emulation. Alias it as 'CONFIG_SYSTEM_ONLY' for parity with user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-8-philmd@linaro.org> Signed-off-by: Richard Henderson --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index cfa98e9e25..3276f738ae 100644 --- a/meson.build +++ b/meson.build @@ -2864,6 +2864,7 @@ foreach target : target_dirs endif config_target += { 'CONFIG_BSD_USER': 'y' } elif target.endswith('softmmu') + config_target += { 'CONFIG_SYSTEM_ONLY': 'y' } config_target += { 'CONFIG_SOFTMMU': 'y' } endif if target.endswith('-user') From c7b64948f8ae4142b65f644164d0678892fe223d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:46 +0200 Subject: [PATCH 405/412] meson: Replace CONFIG_SOFTMMU -> CONFIG_SYSTEM_ONLY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we *might* have user emulation with softmmu, use the clearer 'CONFIG_SYSTEM_ONLY' key to check for system emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-9-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/qtest/meson.build | 2 +- accel/stubs/meson.build | 2 +- accel/tcg/meson.build | 6 +++--- dump/meson.build | 2 +- hw/i386/kvm/meson.build | 2 +- meson.build | 4 ++-- migration/meson.build | 2 +- monitor/meson.build | 2 +- qapi/meson.build | 2 +- semihosting/meson.build | 2 +- softmmu/meson.build | 4 ++-- target/i386/tcg/sysemu/meson.build | 2 +- ui/meson.build | 4 ++-- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/accel/qtest/meson.build b/accel/qtest/meson.build index 176d990ae1..2018de8a05 100644 --- a/accel/qtest/meson.build +++ b/accel/qtest/meson.build @@ -1 +1 @@ -qtest_module_ss.add(when: ['CONFIG_SOFTMMU'], if_true: files('qtest.c')) +qtest_module_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: files('qtest.c')) diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 0249b9258f..f7a9486e06 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -4,4 +4,4 @@ sysemu_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) sysemu_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) sysemu_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) -specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: sysemu_stubs_ss) +specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: sysemu_stubs_ss) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index aeb20a6ef0..166bef173b 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -10,18 +10,18 @@ tcg_ss.add(files( 'translator.c', )) tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) -tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c')) +tcg_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c')) tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c')]) tcg_ss.add(when: libdw, if_true: files('debuginfo.c')) tcg_ss.add(when: 'CONFIG_LINUX', if_true: files('perf.c')) specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', 'monitor.c', )) -tcg_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( +tcg_module_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'tcg-accel-ops.c', 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-icount.c', diff --git a/dump/meson.build b/dump/meson.build index df52ee4268..3ef1f7ce00 100644 --- a/dump/meson.build +++ b/dump/meson.build @@ -1,2 +1,2 @@ softmmu_ss.add([files('dump.c', 'dump-hmp-cmds.c'), snappy, lzo]) -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files('win_dump.c')) +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('win_dump.c')) diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index 6621ba5cd7..ab143d6474 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -19,4 +19,4 @@ xen_stubs_ss.add(when: 'CONFIG_XEN_EMU', if_false: files( 'xen-stubs.c', )) -specific_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: xen_stubs_ss) +specific_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: xen_stubs_ss) diff --git a/meson.build b/meson.build index 3276f738ae..0faca1dc0d 100644 --- a/meson.build +++ b/meson.build @@ -2991,7 +2991,7 @@ config_all += config_host config_all += config_all_disas config_all += { 'CONFIG_XEN': xen.found(), - 'CONFIG_SOFTMMU': have_system, + 'CONFIG_SYSTEM_ONLY': have_system, 'CONFIG_USER_ONLY': have_user, 'CONFIG_ALL': true, } @@ -3665,7 +3665,7 @@ endif softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) -common_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: [softmmu_ss]) +common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [softmmu_ss]) common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) common_all = common_ss.apply(config_all, strict: false) diff --git a/migration/meson.build b/migration/meson.build index 8ba6e420fe..9975407cd0 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -40,6 +40,6 @@ if get_option('live_block_migration').allowed() endif softmmu_ss.add(when: zstd, if_true: files('multifd-zstd.c')) -specific_ss.add(when: 'CONFIG_SOFTMMU', +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('ram.c', 'target.c')) diff --git a/monitor/meson.build b/monitor/meson.build index ccb4d1a8e6..4c0a33ae65 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -7,5 +7,5 @@ softmmu_ss.add(files( )) softmmu_ss.add([spice_headers, files('qmp-cmds.c')]) -specific_ss.add(when: 'CONFIG_SOFTMMU', +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'hmp-cmds-target.c', 'hmp-target.c'), spice]) diff --git a/qapi/meson.build b/qapi/meson.build index 9fd480c4d8..60a668b343 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -141,6 +141,6 @@ foreach output : qapi_specific_outputs + qapi_nonmodule_outputs if output.endswith('.trace-events') qapi_trace_events += qapi_files[i] endif - specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: qapi_files[i]) + specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: qapi_files[i]) i = i + 1 endforeach diff --git a/semihosting/meson.build b/semihosting/meson.build index 8057db5494..b07cbd980f 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -3,7 +3,7 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'syscalls.c', )) -specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( +specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( 'config.c', 'console.c', 'uaccess.c', diff --git a/softmmu/meson.build b/softmmu/meson.build index 974732b0f3..d75f45b7c3 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -1,4 +1,4 @@ -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'arch_init.c', 'ioport.c', 'memory.c', @@ -6,7 +6,7 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( 'watchpoint.c', )]) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: [files( 'icount.c', )]) diff --git a/target/i386/tcg/sysemu/meson.build b/target/i386/tcg/sysemu/meson.build index 2e444e766a..7179bd21fb 100644 --- a/target/i386/tcg/sysemu/meson.build +++ b/target/i386/tcg/sysemu/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: ['CONFIG_TCG', 'CONFIG_SOFTMMU'], if_true: files( +i386_softmmu_ss.add(when: ['CONFIG_TCG', 'CONFIG_SYSTEM_ONLY'], if_true: files( 'tcg-cpu.c', 'smm_helper.c', 'excp_helper.c', diff --git a/ui/meson.build b/ui/meson.build index e09b616a66..125dd41cd6 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -1,6 +1,6 @@ softmmu_ss.add(pixman) -specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: pixman) # for the include path -specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: opengl) # for the include path +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: pixman) # for the include path +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: opengl) # for the include path softmmu_ss.add(png) softmmu_ss.add(files( From de6cd7599b518f0c832cc85980196ec02c129a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 15:33:47 +0200 Subject: [PATCH 406/412] meson: Replace softmmu_ss -> system_ss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use the user_ss[] array to hold the user emulation sources, and the softmmu_ss[] array to hold the system emulation ones. Hold the latter in the 'system_ss[]' array for parity with user emulation. Mechanical change doing: $ sed -i -e s/softmmu_ss/system_ss/g $(git grep -l softmmu_ss) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230613133347.82210-10-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/meson.build | 4 +- audio/meson.build | 8 +- backends/meson.build | 20 ++-- backends/tpm/meson.build | 8 +- block/meson.build | 6 +- block/monitor/meson.build | 2 +- chardev/meson.build | 2 +- disas/meson.build | 2 +- docs/devel/build-system.rst | 14 +-- dump/meson.build | 2 +- ebpf/meson.build | 2 +- fsdev/meson.build | 4 +- gdbstub/meson.build | 10 +- hw/9pfs/meson.build | 2 +- hw/acpi/meson.build | 10 +- hw/adc/meson.build | 10 +- hw/arm/meson.build | 8 +- hw/audio/meson.build | 28 +++--- hw/block/meson.build | 28 +++--- hw/char/meson.build | 70 +++++++------- hw/core/meson.build | 22 ++--- hw/cpu/meson.build | 6 +- hw/cxl/meson.build | 4 +- hw/display/meson.build | 76 +++++++-------- hw/dma/meson.build | 32 +++---- hw/gpio/meson.build | 26 ++--- hw/i2c/meson.build | 2 +- hw/ide/meson.build | 28 +++--- hw/input/meson.build | 32 +++---- hw/intc/meson.build | 44 ++++----- hw/ipack/meson.build | 2 +- hw/ipmi/meson.build | 2 +- hw/isa/meson.build | 18 ++-- hw/mem/meson.build | 8 +- hw/misc/macio/meson.build | 2 +- hw/misc/meson.build | 148 ++++++++++++++--------------- hw/net/can/meson.build | 16 ++-- hw/net/meson.build | 96 +++++++++---------- hw/nubus/meson.build | 2 +- hw/nvme/meson.build | 2 +- hw/nvram/meson.build | 26 ++--- hw/pci-bridge/meson.build | 4 +- hw/pci-host/meson.build | 2 +- hw/pci/meson.build | 8 +- hw/pcmcia/meson.build | 4 +- hw/rdma/meson.build | 2 +- hw/remote/meson.build | 2 +- hw/rtc/meson.build | 28 +++--- hw/scsi/meson.build | 2 +- hw/sd/meson.build | 24 ++--- hw/sensor/meson.build | 18 ++-- hw/smbios/meson.build | 6 +- hw/ssi/meson.build | 26 ++--- hw/timer/meson.build | 74 +++++++-------- hw/tpm/meson.build | 14 +-- hw/usb/meson.build | 74 +++++++-------- hw/virtio/meson.build | 12 +-- hw/watchdog/meson.build | 18 ++-- hw/xen/meson.build | 4 +- meson.build | 12 +-- migration/meson.build | 10 +- monitor/meson.build | 4 +- net/can/meson.build | 2 +- net/meson.build | 38 ++++---- qom/meson.build | 2 +- replay/meson.build | 2 +- softmmu/meson.build | 8 +- stats/meson.build | 2 +- target/alpha/meson.build | 6 +- target/arm/hvf/meson.build | 2 +- target/arm/meson.build | 6 +- target/arm/tcg/meson.build | 2 +- target/avr/meson.build | 6 +- target/cris/meson.build | 6 +- target/hppa/meson.build | 6 +- target/i386/hax/meson.build | 6 +- target/i386/hvf/meson.build | 2 +- target/i386/kvm/meson.build | 4 +- target/i386/meson.build | 8 +- target/i386/nvmm/meson.build | 4 +- target/i386/tcg/sysemu/meson.build | 2 +- target/i386/whpx/meson.build | 2 +- target/loongarch/meson.build | 6 +- target/m68k/meson.build | 6 +- target/microblaze/meson.build | 6 +- target/mips/meson.build | 4 +- target/mips/sysemu/meson.build | 2 +- target/mips/tcg/sysemu/meson.build | 2 +- target/nios2/meson.build | 6 +- target/openrisc/meson.build | 6 +- target/ppc/meson.build | 10 +- target/riscv/meson.build | 6 +- target/s390x/kvm/meson.build | 2 +- target/s390x/meson.build | 6 +- target/sh4/meson.build | 6 +- target/sparc/meson.build | 6 +- target/tricore/meson.build | 4 +- target/xtensa/meson.build | 6 +- tcg/meson.build | 2 +- trace/meson.build | 2 +- ui/meson.build | 26 ++--- 101 files changed, 706 insertions(+), 706 deletions(-) diff --git a/accel/meson.build b/accel/meson.build index 49558dd232..638a9a03ba 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -1,5 +1,5 @@ specific_ss.add(files('accel-common.c', 'accel-blocker.c')) -softmmu_ss.add(files('accel-softmmu.c')) +system_ss.add(files('accel-softmmu.c')) user_ss.add(files('accel-user.c')) subdir('tcg') @@ -12,4 +12,4 @@ if have_system endif # qtest -softmmu_ss.add(files('dummy-cpus.c')) +system_ss.add(files('dummy-cpus.c')) diff --git a/audio/meson.build b/audio/meson.build index 65a49c1a10..e7e95cf751 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -1,5 +1,5 @@ -softmmu_ss.add([spice_headers, files('audio.c')]) -softmmu_ss.add(files( +system_ss.add([spice_headers, files('audio.c')]) +system_ss.add(files( 'audio-hmp-cmds.c', 'audio_legacy.c', 'mixeng.c', @@ -8,8 +8,8 @@ softmmu_ss.add(files( 'wavcapture.c', )) -softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.m')) -softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) +system_ss.add(when: coreaudio, if_true: files('coreaudio.m')) +system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) audio_modules = {} foreach m : [ diff --git a/backends/meson.build b/backends/meson.build index b369e0a9d0..914c7c4afb 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add([files( +system_ss.add([files( 'cryptodev-builtin.c', 'cryptodev-hmp-cmds.c', 'cryptodev.c', @@ -10,20 +10,20 @@ softmmu_ss.add([files( 'confidential-guest-support.c', ), numa]) -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c')) -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c')) -softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c')) +system_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c')) +system_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c')) +system_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c')) if keyutils.found() - softmmu_ss.add(keyutils, files('cryptodev-lkcf.c')) + system_ss.add(keyutils, files('cryptodev-lkcf.c')) endif if have_vhost_user - softmmu_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c')) + system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c')) endif -softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) +system_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) if have_vhost_user_crypto - softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost-user.c')) + system_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost-user.c')) endif -softmmu_ss.add(when: gio, if_true: files('dbus-vmstate.c')) -softmmu_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) +system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) +system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) subdir('tpm') diff --git a/backends/tpm/meson.build b/backends/tpm/meson.build index 7f2503f84e..0bfa6c422b 100644 --- a/backends/tpm/meson.build +++ b/backends/tpm/meson.build @@ -1,6 +1,6 @@ if have_tpm - softmmu_ss.add(files('tpm_backend.c')) - softmmu_ss.add(files('tpm_util.c')) - softmmu_ss.add(when: 'CONFIG_TPM_PASSTHROUGH', if_true: files('tpm_passthrough.c')) - softmmu_ss.add(when: 'CONFIG_TPM_EMULATOR', if_true: files('tpm_emulator.c')) + system_ss.add(files('tpm_backend.c')) + system_ss.add(files('tpm_util.c')) + system_ss.add(when: 'CONFIG_TPM_PASSTHROUGH', if_true: files('tpm_passthrough.c')) + system_ss.add(when: 'CONFIG_TPM_EMULATOR', if_true: files('tpm_emulator.c')) endif diff --git a/block/meson.build b/block/meson.build index fb4332bd66..529fc172c6 100644 --- a/block/meson.build +++ b/block/meson.build @@ -42,8 +42,8 @@ block_ss.add(files( 'write-threshold.c', ), zstd, zlib, gnutls) -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) -softmmu_ss.add(files('block-ram-registrar.c')) +system_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) +system_ss.add(files('block-ram-registrar.c')) if get_option('qcow1').allowed() block_ss.add(files('qcow.c')) @@ -159,7 +159,7 @@ block_ss.add(block_gen_c) block_ss.add(files('stream.c')) -softmmu_ss.add(files('qapi-sysemu.c')) +system_ss.add(files('qapi-sysemu.c')) subdir('export') subdir('monitor') diff --git a/block/monitor/meson.build b/block/monitor/meson.build index 374aac1140..1022516e93 100644 --- a/block/monitor/meson.build +++ b/block/monitor/meson.build @@ -1,2 +1,2 @@ -softmmu_ss.add(files('block-hmp-cmds.c')) +system_ss.add(files('block-hmp-cmds.c')) block_ss.add(files('bitmap-qmp-cmds.c')) diff --git a/chardev/meson.build b/chardev/meson.build index 7a3ba777ab..fb630b429e 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -28,7 +28,7 @@ chardev_ss.add(when: 'CONFIG_WIN32', if_true: files( chardev_ss = chardev_ss.apply(config_host, strict: false) -softmmu_ss.add(files( +system_ss.add(files( 'char-hmp-cmds.c', 'msmouse.c', 'wctablet.c', diff --git a/disas/meson.build b/disas/meson.build index 832727e4b3..3a480eb9f8 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -13,5 +13,5 @@ common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) common_ss.add(when: capstone, if_true: [files('capstone.c'), capstone]) common_ss.add(files('disas.c')) -softmmu_ss.add(files('disas-mon.c')) +system_ss.add(files('disas-mon.c')) specific_ss.add(capstone) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 551c5a5ac0..64efa26b90 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -183,9 +183,9 @@ Target-independent emulator sourcesets: This includes error handling infrastructure, standard data structures, platform portability wrapper functions, etc. - Target-independent code lives in the ``common_ss``, ``softmmu_ss`` and + Target-independent code lives in the ``common_ss``, ``system_ss`` and ``user_ss`` sourcesets. ``common_ss`` is linked into all emulators, - ``softmmu_ss`` only in system emulators, ``user_ss`` only in user-mode + ``system_ss`` only in system emulators, ``user_ss`` only in user-mode emulators. Target-independent sourcesets must exercise particular care when using @@ -197,11 +197,11 @@ Target-independent emulator sourcesets: symbol:: # Some targets have CONFIG_ACPI, some don't, so this is not enough - softmmu_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi.c'), + system_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi.c'), if_false: files('acpi-stub.c')) # This is required as well: - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c')) + system_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c')) Target-dependent emulator sourcesets: In the target-dependent set lives CPU emulation, some device emulation and @@ -229,16 +229,16 @@ Target-dependent emulator sourcesets: for all emulators and for system emulators only. For example:: arm_ss = ss.source_set() - arm_softmmu_ss = ss.source_set() + arm_system_ss = ss.source_set() ... target_arch += {'arm': arm_ss} - target_softmmu_arch += {'arm': arm_softmmu_ss} + target_softmmu_arch += {'arm': arm_system_ss} Module sourcesets: There are two dictionaries for modules: ``modules`` is used for target-independent modules and ``target_modules`` is used for target-dependent modules. When modules are disabled the ``module`` - source sets are added to ``softmmu_ss`` and the ``target_modules`` + source sets are added to ``system_ss`` and the ``target_modules`` source sets are added to ``specific_ss``. Both dictionaries are nested. One dictionary is created per diff --git a/dump/meson.build b/dump/meson.build index 3ef1f7ce00..4277ce9328 100644 --- a/dump/meson.build +++ b/dump/meson.build @@ -1,2 +1,2 @@ -softmmu_ss.add([files('dump.c', 'dump-hmp-cmds.c'), snappy, lzo]) +system_ss.add([files('dump.c', 'dump-hmp-cmds.c'), snappy, lzo]) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('win_dump.c')) diff --git a/ebpf/meson.build b/ebpf/meson.build index 2dd0fd8948..2f627d6c7d 100644 --- a/ebpf/meson.build +++ b/ebpf/meson.build @@ -1 +1 @@ -softmmu_ss.add(when: libbpf, if_true: files('ebpf_rss.c'), if_false: files('ebpf_rss-stub.c')) +system_ss.add(when: libbpf, if_true: files('ebpf_rss.c'), if_false: files('ebpf_rss-stub.c')) diff --git a/fsdev/meson.build b/fsdev/meson.build index b632b66348..1bec065924 100644 --- a/fsdev/meson.build +++ b/fsdev/meson.build @@ -6,8 +6,8 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files( '9p-marshal.c', 'qemu-fsdev.c', ), if_false: files('qemu-fsdev-dummy.c')) -softmmu_ss.add_all(when: 'CONFIG_LINUX', if_true: fsdev_ss) -softmmu_ss.add_all(when: 'CONFIG_DARWIN', if_true: fsdev_ss) +system_ss.add_all(when: 'CONFIG_LINUX', if_true: fsdev_ss) +system_ss.add_all(when: 'CONFIG_DARWIN', if_true: fsdev_ss) if have_virtfs_proxy_helper executable('virtfs-proxy-helper', diff --git a/gdbstub/meson.build b/gdbstub/meson.build index cdb4d28691..77762e0b3e 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -8,14 +8,14 @@ # cflags so: gdb_user_ss = ss.source_set() -gdb_softmmu_ss = ss.source_set() +gdb_system_ss = ss.source_set() # We build two versions of gdbstub, one for each mode gdb_user_ss.add(files('gdbstub.c', 'user.c')) -gdb_softmmu_ss.add(files('gdbstub.c', 'softmmu.c')) +gdb_system_ss.add(files('gdbstub.c', 'softmmu.c')) gdb_user_ss = gdb_user_ss.apply(config_host, strict: false) -gdb_softmmu_ss = gdb_softmmu_ss.apply(config_host, strict: false) +gdb_system_ss = gdb_system_ss.apply(config_host, strict: false) libgdb_user = static_library('gdb_user', gdb_user_ss.sources() + genh, @@ -24,14 +24,14 @@ libgdb_user = static_library('gdb_user', build_by_default: have_user) libgdb_softmmu = static_library('gdb_softmmu', - gdb_softmmu_ss.sources() + genh, + gdb_system_ss.sources() + genh, name_suffix: 'fa', build_by_default: have_system) gdb_user = declare_dependency(link_whole: libgdb_user) user_ss.add(gdb_user) gdb_softmmu = declare_dependency(link_whole: libgdb_softmmu) -softmmu_ss.add(gdb_softmmu) +system_ss.add(gdb_softmmu) common_ss.add(files('syscalls.c')) diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index fd37b7a02d..2944ea63c3 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -16,6 +16,6 @@ fs_ss.add(files( fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-util-linux.c')) fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-util-darwin.c')) fs_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-9p-backend.c')) -softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) +system_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) specific_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-device.c')) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index e0bf39bf4c..fc1b952379 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -30,12 +30,12 @@ acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) if have_tpm acpi_ss.add(files('tpm.c')) endif -softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c')) -softmmu_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-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', +system_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c')) +system_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c')) +system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) +system_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c', 'acpi-x86-stub.c', 'ipmi-stub.c', 'ghes-stub.c', 'acpi-mem-hotplug-stub.c', 'acpi-cpu-hotplug-stub.c', 'acpi-pci-hotplug-stub.c', 'acpi-nvdimm-stub.c', 'cxl-stub.c', 'pci-bridge-stub.c')) -softmmu_ss.add(files('acpi-qmp-cmds.c')) +system_ss.add(files('acpi-qmp-cmds.c')) diff --git a/hw/adc/meson.build b/hw/adc/meson.build index b29ac7ccdf..a4f85b7d46 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,5 +1,5 @@ -softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) -softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) +system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) +system_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 4f94f821b0..11eb9112f8 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -66,9 +66,9 @@ arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c')) arm_ss.add_all(xen_ss) -softmmu_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) -softmmu_ss.add(when: 'CONFIG_TOSA', if_true: files('tosa.c')) +system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) +system_ss.add(when: 'CONFIG_TOSA', if_true: files('tosa.c')) hw_arch += {'arm': arm_ss} diff --git a/hw/audio/meson.build b/hw/audio/meson.build index e48a9fc73d..d0fda1009e 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -1,14 +1,14 @@ -softmmu_ss.add(files('soundhw.c')) -softmmu_ss.add(when: 'CONFIG_AC97', if_true: files('ac97.c')) -softmmu_ss.add(when: 'CONFIG_ADLIB', if_true: files('fmopl.c', 'adlib.c')) -softmmu_ss.add(when: 'CONFIG_CS4231', if_true: files('cs4231.c')) -softmmu_ss.add(when: 'CONFIG_CS4231A', if_true: files('cs4231a.c')) -softmmu_ss.add(when: 'CONFIG_ES1370', if_true: files('es1370.c')) -softmmu_ss.add(when: 'CONFIG_GUS', if_true: files('gus.c', 'gusemu_hal.c', 'gusemu_mixer.c')) -softmmu_ss.add(when: 'CONFIG_HDA', if_true: files('intel-hda.c', 'hda-codec.c')) -softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('marvell_88w8618.c')) -softmmu_ss.add(when: 'CONFIG_PCSPK', if_true: files('pcspk.c')) -softmmu_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c')) -softmmu_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) -softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) -softmmu_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) +system_ss.add(files('soundhw.c')) +system_ss.add(when: 'CONFIG_AC97', if_true: files('ac97.c')) +system_ss.add(when: 'CONFIG_ADLIB', if_true: files('fmopl.c', 'adlib.c')) +system_ss.add(when: 'CONFIG_CS4231', if_true: files('cs4231.c')) +system_ss.add(when: 'CONFIG_CS4231A', if_true: files('cs4231a.c')) +system_ss.add(when: 'CONFIG_ES1370', if_true: files('es1370.c')) +system_ss.add(when: 'CONFIG_GUS', if_true: files('gus.c', 'gusemu_hal.c', 'gusemu_mixer.c')) +system_ss.add(when: 'CONFIG_HDA', if_true: files('intel-hda.c', 'hda-codec.c')) +system_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('marvell_88w8618.c')) +system_ss.add(when: 'CONFIG_PCSPK', if_true: files('pcspk.c')) +system_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c')) +system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) +system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) +system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) diff --git a/hw/block/meson.build b/hw/block/meson.build index cc2a75cc50..8aa4dc3893 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -1,21 +1,21 @@ -softmmu_ss.add(files( +system_ss.add(files( 'block.c', 'cdrom.c', 'hd-geometry.c' )) -softmmu_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c')) -softmmu_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c')) -softmmu_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c')) -softmmu_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c')) -softmmu_ss.add(when: 'CONFIG_NAND', if_true: files('nand.c')) -softmmu_ss.add(when: 'CONFIG_ONENAND', if_true: files('onenand.c')) -softmmu_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files('pflash_cfi01.c')) -softmmu_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c')) -softmmu_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) -softmmu_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) -softmmu_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) -softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) -softmmu_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c')) +system_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c')) +system_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c')) +system_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c')) +system_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c')) +system_ss.add(when: 'CONFIG_NAND', if_true: files('nand.c')) +system_ss.add(when: 'CONFIG_ONENAND', if_true: files('onenand.c')) +system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files('pflash_cfi01.c')) +system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c')) +system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) +system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) +system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) +system_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c')) specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c', 'virtio-blk-common.c')) specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c', 'virtio-blk-common.c')) diff --git a/hw/char/meson.build b/hw/char/meson.build index 0807e00ae4..006d20f1e2 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -1,39 +1,39 @@ -softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_uart.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_UART', if_true: files('cmsdk-apb-uart.c')) -softmmu_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_ser.c')) -softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) -softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) -softmmu_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) -softmmu_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) -softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) -softmmu_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) -softmmu_ss.add(when: 'CONFIG_PL011', if_true: files('pl011.c')) -softmmu_ss.add(when: 'CONFIG_SCLPCONSOLE', if_true: files('sclpconsole.c', 'sclpconsole-lm.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL_PCI', if_true: files('serial-pci.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL_PCI_MULTI', if_true: files('serial-pci-multi.c')) -softmmu_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c')) -softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c')) -softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c')) +system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_uart.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_UART', if_true: files('cmsdk-apb-uart.c')) +system_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_ser.c')) +system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) +system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) +system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) +system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) +system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) +system_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) +system_ss.add(when: 'CONFIG_PL011', if_true: files('pl011.c')) +system_ss.add(when: 'CONFIG_SCLPCONSOLE', if_true: files('sclpconsole.c', 'sclpconsole-lm.c')) +system_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c')) +system_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c')) +system_ss.add(when: 'CONFIG_SERIAL_PCI', if_true: files('serial-pci.c')) +system_ss.add(when: 'CONFIG_SERIAL_PCI_MULTI', if_true: files('serial-pci-multi.c')) +system_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c')) +system_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c')) +system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c')) -softmmu_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c')) -softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c')) -softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) -softmmu_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) -softmmu_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) -softmmu_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) +system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c')) +system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c')) +system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) +system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) +system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) +system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) +system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) +system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c')) specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c')) diff --git a/hw/core/meson.build b/hw/core/meson.build index 959bc924d4..67dad04de5 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -24,18 +24,18 @@ endif common_ss.add(files('cpu-common.c')) common_ss.add(files('machine-smp.c')) -softmmu_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) -softmmu_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) -softmmu_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) -softmmu_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) -softmmu_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) -softmmu_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) -softmmu_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) -softmmu_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) -softmmu_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) +system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) +system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) +system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) +system_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) +system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) +system_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) +system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) +system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) +system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) +system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) -softmmu_ss.add(files( +system_ss.add(files( 'cpu-sysemu.c', 'fw-path-provider.c', 'gpio.c', diff --git a/hw/cpu/meson.build b/hw/cpu/meson.build index e37490074f..6d319947ca 100644 --- a/hw/cpu/meson.build +++ b/hw/cpu/meson.build @@ -1,6 +1,6 @@ -softmmu_ss.add(files('core.c', 'cluster.c')) +system_ss.add(files('core.c', 'cluster.c')) -softmmu_ss.add(when: 'CONFIG_ARM11MPCORE', if_true: files('arm11mpcore.c')) -softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_mpcore.c')) +system_ss.add(when: 'CONFIG_ARM11MPCORE', if_true: files('arm11mpcore.c')) +system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_mpcore.c')) specific_ss.add(when: 'CONFIG_A9MPCORE', if_true: files('a9mpcore.c')) specific_ss.add(when: 'CONFIG_A15MPCORE', if_true: files('a15mpcore.c')) diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build index cfa95ffd40..1f9aa2ea1f 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(when: 'CONFIG_CXL', +system_ss.add(when: 'CONFIG_CXL', if_true: files( 'cxl-component-utils.c', 'cxl-device-utils.c', @@ -10,4 +10,4 @@ softmmu_ss.add(when: 'CONFIG_CXL', 'cxl-host-stubs.c', )) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('cxl-host-stubs.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('cxl-host-stubs.c')) diff --git a/hw/display/meson.build b/hw/display/meson.build index 17165bd536..413ba4ab24 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -1,49 +1,49 @@ hw_display_modules = {} -softmmu_ss.add(when: 'CONFIG_DDC', if_true: files('i2c-ddc.c')) -softmmu_ss.add(when: 'CONFIG_EDID', if_true: files('edid-generate.c', 'edid-region.c')) +system_ss.add(when: 'CONFIG_DDC', if_true: files('i2c-ddc.c')) +system_ss.add(when: 'CONFIG_EDID', if_true: files('edid-generate.c', 'edid-region.c')) -softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c')) -softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb-standalone.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb-standalone.c')) -softmmu_ss.add(when: 'CONFIG_VGA_CIRRUS', if_true: files('cirrus_vga.c')) -softmmu_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cirrus_vga_isa.c')) -softmmu_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c')) -softmmu_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c')) -softmmu_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c')) -softmmu_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c')) -softmmu_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c')) -softmmu_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c')) -softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xenfb.c')) +system_ss.add(when: 'CONFIG_VGA_CIRRUS', if_true: files('cirrus_vga.c')) +system_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cirrus_vga_isa.c')) +system_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c')) +system_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c')) +system_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c')) +system_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c')) +system_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c')) +system_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xenfb.c')) -softmmu_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c')) -softmmu_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c')) -softmmu_ss.add(when: 'CONFIG_VGA_MMIO', if_true: files('vga-mmio.c')) -softmmu_ss.add(when: 'CONFIG_VMWARE_VGA', if_true: files('vmware_vga.c')) -softmmu_ss.add(when: 'CONFIG_BOCHS_DISPLAY', if_true: files('bochs-display.c')) +system_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c')) +system_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c')) +system_ss.add(when: 'CONFIG_VGA_MMIO', if_true: files('vga-mmio.c')) +system_ss.add(when: 'CONFIG_VMWARE_VGA', if_true: files('vmware_vga.c')) +system_ss.add(when: 'CONFIG_BOCHS_DISPLAY', if_true: files('bochs-display.c')) -softmmu_ss.add(when: 'CONFIG_BLIZZARD', if_true: files('blizzard.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_fimd.c')) -softmmu_ss.add(when: 'CONFIG_FRAMEBUFFER', if_true: files('framebuffer.c')) -softmmu_ss.add(when: 'CONFIG_ZAURUS', if_true: files('tc6393xb.c')) +system_ss.add(when: 'CONFIG_BLIZZARD', if_true: files('blizzard.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_fimd.c')) +system_ss.add(when: 'CONFIG_FRAMEBUFFER', if_true: files('framebuffer.c')) +system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('tc6393xb.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dss.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_lcd.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_fb.c')) -softmmu_ss.add(when: 'CONFIG_SM501', if_true: files('sm501.c')) -softmmu_ss.add(when: 'CONFIG_TCX', if_true: files('tcx.c')) -softmmu_ss.add(when: 'CONFIG_CG3', if_true: files('cg3.c')) -softmmu_ss.add(when: 'CONFIG_MACFB', if_true: files('macfb.c')) -softmmu_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dss.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_lcd.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_fb.c')) +system_ss.add(when: 'CONFIG_SM501', if_true: files('sm501.c')) +system_ss.add(when: 'CONFIG_TCX', if_true: files('tcx.c')) +system_ss.add(when: 'CONFIG_CG3', if_true: files('cg3.c')) +system_ss.add(when: 'CONFIG_MACFB', if_true: files('macfb.c')) +system_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c')) -softmmu_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) +system_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) if (config_all_devices.has_key('CONFIG_VGA_CIRRUS') or config_all_devices.has_key('CONFIG_VGA_PCI') or config_all_devices.has_key('CONFIG_VMWARE_VGA') or config_all_devices.has_key('CONFIG_ATI_VGA') ) - softmmu_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + system_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), if_false: files('acpi-vga-stub.c')) endif @@ -56,12 +56,12 @@ if config_all_devices.has_key('CONFIG_QXL') hw_display_modules += {'qxl': qxl_ss} endif -softmmu_ss.add(when: 'CONFIG_DPCD', if_true: files('dpcd.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dp.c')) +system_ss.add(when: 'CONFIG_DPCD', if_true: files('dpcd.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dp.c')) -softmmu_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) +system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) -softmmu_ss.add(when: [pixman, 'CONFIG_ATI_VGA'], if_true: files('ati.c', 'ati_2d.c', 'ati_dbg.c')) +system_ss.add(when: [pixman, 'CONFIG_ATI_VGA'], if_true: files('ati.c', 'ati_2d.c', 'ati_dbg.c')) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') @@ -115,7 +115,7 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA') hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss} endif -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-vga-stub.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-vga-stub.c')) modules += { 'hw-display': hw_display_modules } diff --git a/hw/dma/meson.build b/hw/dma/meson.build index f3f0661bc3..a96c1be2c8 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -1,16 +1,16 @@ -softmmu_ss.add(when: 'CONFIG_RC4030', if_true: files('rc4030.c')) -softmmu_ss.add(when: 'CONFIG_PL080', if_true: files('pl080.c')) -softmmu_ss.add(when: 'CONFIG_PL330', if_true: files('pl330.c')) -softmmu_ss.add(when: 'CONFIG_I82374', if_true: files('i82374.c')) -softmmu_ss.add(when: 'CONFIG_I8257', if_true: files('i8257.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axidma.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: files('xlnx-zynq-devcfg.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) -softmmu_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dpdma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZDMA', if_true: files('xlnx-zdma.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) +system_ss.add(when: 'CONFIG_RC4030', if_true: files('rc4030.c')) +system_ss.add(when: 'CONFIG_PL080', if_true: files('pl080.c')) +system_ss.add(when: 'CONFIG_PL330', if_true: files('pl330.c')) +system_ss.add(when: 'CONFIG_I82374', if_true: files('i82374.c')) +system_ss.add(when: 'CONFIG_I8257', if_true: files('i8257.c')) +system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axidma.c')) +system_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: files('xlnx-zynq-devcfg.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) +system_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dpdma.c')) +system_ss.add(when: 'CONFIG_XLNX_ZDMA', if_true: files('xlnx-zdma.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) +system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) +system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index b726e6d27a..066ea96480 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -1,14 +1,14 @@ -softmmu_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c')) -softmmu_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c')) -softmmu_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c')) -softmmu_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c')) -softmmu_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c')) -softmmu_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c')) +system_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c')) +system_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c')) +system_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c')) +system_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c')) +system_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c')) +system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) +system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index 3996564c25..b58bc167db 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -17,4 +17,4 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c')) -softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) +system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) diff --git a/hw/ide/meson.build b/hw/ide/meson.build index ddcb3b28d2..e050eef942 100644 --- a/hw/ide/meson.build +++ b/hw/ide/meson.build @@ -1,14 +1,14 @@ -softmmu_ss.add(when: 'CONFIG_AHCI', if_true: files('ahci.c')) -softmmu_ss.add(when: 'CONFIG_AHCI_ICH9', if_true: files('ich.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('ahci-allwinner.c')) -softmmu_ss.add(when: 'CONFIG_IDE_CMD646', if_true: files('cmd646.c')) -softmmu_ss.add(when: 'CONFIG_IDE_CORE', if_true: files('core.c', 'atapi.c')) -softmmu_ss.add(when: 'CONFIG_IDE_ISA', if_true: files('isa.c', 'ioport.c')) -softmmu_ss.add(when: 'CONFIG_IDE_MACIO', if_true: files('macio.c')) -softmmu_ss.add(when: 'CONFIG_IDE_MMIO', if_true: files('mmio.c')) -softmmu_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c')) -softmmu_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c')) -softmmu_ss.add(when: 'CONFIG_IDE_QDEV', if_true: files('qdev.c')) -softmmu_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c')) -softmmu_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c')) -softmmu_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c')) +system_ss.add(when: 'CONFIG_AHCI', if_true: files('ahci.c')) +system_ss.add(when: 'CONFIG_AHCI_ICH9', if_true: files('ich.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('ahci-allwinner.c')) +system_ss.add(when: 'CONFIG_IDE_CMD646', if_true: files('cmd646.c')) +system_ss.add(when: 'CONFIG_IDE_CORE', if_true: files('core.c', 'atapi.c')) +system_ss.add(when: 'CONFIG_IDE_ISA', if_true: files('isa.c', 'ioport.c')) +system_ss.add(when: 'CONFIG_IDE_MACIO', if_true: files('macio.c')) +system_ss.add(when: 'CONFIG_IDE_MMIO', if_true: files('mmio.c')) +system_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c')) +system_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c')) +system_ss.add(when: 'CONFIG_IDE_QDEV', if_true: files('qdev.c')) +system_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c')) +system_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c')) +system_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c')) diff --git a/hw/input/meson.build b/hw/input/meson.build index 8deb011d4a..c0d4482180 100644 --- a/hw/input/meson.build +++ b/hw/input/meson.build @@ -1,18 +1,18 @@ -softmmu_ss.add(files('hid.c')) -softmmu_ss.add(when: 'CONFIG_ADB', if_true: files('adb.c', 'adb-mouse.c', 'adb-kbd.c')) -softmmu_ss.add(when: 'CONFIG_ADS7846', if_true: files('ads7846.c')) -softmmu_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) -softmmu_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) -softmmu_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) -softmmu_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) -softmmu_ss.add(when: 'CONFIG_STELLARIS_INPUT', if_true: files('stellaris_input.c')) -softmmu_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) +system_ss.add(files('hid.c')) +system_ss.add(when: 'CONFIG_ADB', if_true: files('adb.c', 'adb-mouse.c', 'adb-kbd.c')) +system_ss.add(when: 'CONFIG_ADS7846', if_true: files('ads7846.c')) +system_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) +system_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) +system_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) +system_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) +system_ss.add(when: 'CONFIG_STELLARIS_INPUT', if_true: files('stellaris_input.c')) +system_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-hid.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host.c')) -softmmu_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input.c')) +system_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input.c')) +system_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-hid.c')) +system_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host.c')) +system_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c')) -softmmu_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c')) -softmmu_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c')) +system_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c')) +system_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c')) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 8be459b41c..ed355941d1 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -1,40 +1,40 @@ -softmmu_ss.add(files('intc.c')) -softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( +system_ss.add(files('intc.c')) +system_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( 'arm_gic.c', 'arm_gic_common.c', 'arm_gicv2m.c', 'arm_gicv3_common.c', 'arm_gicv3_its_common.c', )) -softmmu_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( +system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( 'arm_gicv3.c', 'arm_gicv3_dist.c', 'arm_gicv3_its.c', 'arm_gicv3_redist.c', )) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_gic.c', 'exynos4210_combiner.c')) -softmmu_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) -softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) -softmmu_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) -softmmu_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) -softmmu_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) -softmmu_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) -softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c')) -softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c')) -softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_intc.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-ipi.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_PMU', if_true: files('xlnx-pmu-iomod-intc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_gic.c', 'exynos4210_combiner.c')) +system_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) +system_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) +system_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) +system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) +system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) +system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) +system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c')) +system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c')) +system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_intc.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-ipi.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_PMU', if_true: files('xlnx-pmu-iomod-intc.c')) if config_all_devices.has_key('CONFIG_APIC') or \ config_all_devices.has_key('CONFIG_I8259') or \ config_all_devices.has_key('CONFIG_MC146818RTC') - softmmu_ss.add(files('kvm_irqcount.c')) + system_ss.add(files('kvm_irqcount.c')) endif specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) diff --git a/hw/ipack/meson.build b/hw/ipack/meson.build index 3f8138b6f2..26567f1068 100644 --- a/hw/ipack/meson.build +++ b/hw/ipack/meson.build @@ -1 +1 @@ -softmmu_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c', 'tpci200.c')) +system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c', 'tpci200.c')) diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build index 9622ea2a2c..07f109d365 100644 --- a/hw/ipmi/meson.build +++ b/hw/ipmi/meson.build @@ -8,4 +8,4 @@ ipmi_ss.add(when: 'CONFIG_ISA_IPMI_BT', if_true: files('isa_ipmi_bt.c')) ipmi_ss.add(when: 'CONFIG_PCI_IPMI_BT', if_true: files('pci_ipmi_bt.c')) ipmi_ss.add(when: 'CONFIG_IPMI_SSIF', if_true: files('smbus_ipmi.c')) -softmmu_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss) +system_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss) diff --git a/hw/isa/meson.build b/hw/isa/meson.build index 8bf678ca0a..b855e81276 100644 --- a/hw/isa/meson.build +++ b/hw/isa/meson.build @@ -1,11 +1,11 @@ -softmmu_ss.add(when: 'CONFIG_APM', if_true: files('apm.c')) -softmmu_ss.add(when: 'CONFIG_I82378', if_true: files('i82378.c')) -softmmu_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('isa-bus.c')) -softmmu_ss.add(when: 'CONFIG_ISA_SUPERIO', if_true: files('isa-superio.c')) -softmmu_ss.add(when: 'CONFIG_PC87312', if_true: files('pc87312.c')) -softmmu_ss.add(when: 'CONFIG_PIIX3', if_true: files('piix3.c')) -softmmu_ss.add(when: 'CONFIG_PIIX4', if_true: files('piix4.c')) -softmmu_ss.add(when: 'CONFIG_SMC37C669', if_true: files('smc37c669-superio.c')) -softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686.c')) +system_ss.add(when: 'CONFIG_APM', if_true: files('apm.c')) +system_ss.add(when: 'CONFIG_I82378', if_true: files('i82378.c')) +system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('isa-bus.c')) +system_ss.add(when: 'CONFIG_ISA_SUPERIO', if_true: files('isa-superio.c')) +system_ss.add(when: 'CONFIG_PC87312', if_true: files('pc87312.c')) +system_ss.add(when: 'CONFIG_PIIX3', if_true: files('piix3.c')) +system_ss.add(when: 'CONFIG_PIIX4', if_true: files('piix4.c')) +system_ss.add(when: 'CONFIG_SMC37C669', if_true: files('smc37c669-superio.c')) +system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686.c')) specific_ss.add(when: 'CONFIG_LPC_ICH9', if_true: files('lpc_ich9.c')) diff --git a/hw/mem/meson.build b/hw/mem/meson.build index 56c2618b84..ec26ef5544 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -4,9 +4,9 @@ mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c')) mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c')) mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c')) mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c')) -softmmu_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_false: files('cxl_type3_stubs.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('cxl_type3_stubs.c')) +system_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_false: files('cxl_type3_stubs.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('cxl_type3_stubs.c')) -softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) +system_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) -softmmu_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) +system_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) diff --git a/hw/misc/macio/meson.build b/hw/misc/macio/meson.build index 17282da20a..8984b818b0 100644 --- a/hw/misc/macio/meson.build +++ b/hw/misc/macio/meson.build @@ -5,4 +5,4 @@ macio_ss.add(when: 'CONFIG_MACIO_GPIO', if_true: files('gpio.c')) macio_ss.add(when: 'CONFIG_MAC_DBDMA', if_true: files('mac_dbdma.c')) macio_ss.add(when: 'CONFIG_MAC_PMU', if_true: files('pmu.c')) -softmmu_ss.add_all(when: 'CONFIG_MACIO', if_true: macio_ss) +system_ss.add_all(when: 'CONFIG_MACIO', if_true: macio_ss) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 78ca857c9d..05877f61cc 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -1,58 +1,58 @@ -softmmu_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) -softmmu_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) -softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) -softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) -softmmu_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) -softmmu_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) -softmmu_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) -softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) -softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) -softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) -softmmu_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) +system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) +system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) +system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) +system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) +system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) +system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) +system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) +system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) +system_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) +system_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) # ARM devices -softmmu_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) -softmmu_ss.add(when: 'CONFIG_INTEGRATOR_DEBUG', if_true: files('arm_integrator_debug.c')) -softmmu_ss.add(when: 'CONFIG_A9SCU', if_true: files('a9scu.c')) -softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) +system_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) +system_ss.add(when: 'CONFIG_INTEGRATOR_DEBUG', if_true: files('arm_integrator_debug.c')) +system_ss.add(when: 'CONFIG_A9SCU', if_true: files('a9scu.c')) +system_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) -softmmu_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c')) +system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c')) # Mac devices -softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) +system_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) # virt devices -softmmu_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) +system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) # RISC-V devices -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) +system_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) +system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) +system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) +system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) subdir('macio') -softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) +system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c')) specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c')) -softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) -softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) -softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) -softmmu_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files( +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c')) +system_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) +system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) +system_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) +system_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx25_ccm.c', 'imx31_ccm.c', 'imx6_ccm.c', @@ -64,22 +64,22 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_ccm.c', 'imx_rngc.c', )) -softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( +system_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm7xx_clk.c', 'npcm7xx_gcr.c', 'npcm7xx_mft.c', 'npcm7xx_pwm.c', 'npcm7xx_rng.c', )) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files( +system_ss.add(when: 'CONFIG_OMAP', if_true: files( 'omap_clk.c', 'omap_gpmc.c', 'omap_l4.c', 'omap_sdrc.c', 'omap_tap.c', )) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( +system_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_mbox.c', 'bcm2835_mphi.c', 'bcm2835_property.c', @@ -88,35 +88,35 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_cprman.c', 'bcm2835_powermgt.c', )) -softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-crf.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-apu-ctrl.c')) +system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) +system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-crf.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-apu-ctrl.c')) specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-crl.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( 'xlnx-versal-xramc.c', 'xlnx-versal-pmc-iou-slcr.c', )) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) -softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) -softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) -softmmu_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c')) -softmmu_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) +system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) +system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) +system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c')) +system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c')) -softmmu_ss.add(when: 'CONFIG_TZ_MPC', if_true: files('tz-mpc.c')) -softmmu_ss.add(when: 'CONFIG_TZ_MSC', if_true: files('tz-msc.c')) -softmmu_ss.add(when: 'CONFIG_TZ_PPC', if_true: files('tz-ppc.c')) -softmmu_ss.add(when: 'CONFIG_IOTKIT_SECCTL', if_true: files('iotkit-secctl.c')) -softmmu_ss.add(when: 'CONFIG_IOTKIT_SYSCTL', if_true: files('iotkit-sysctl.c')) -softmmu_ss.add(when: 'CONFIG_IOTKIT_SYSINFO', if_true: files('iotkit-sysinfo.c')) -softmmu_ss.add(when: 'CONFIG_ARMSSE_CPU_PWRCTRL', if_true: files('armsse-cpu-pwrctrl.c')) -softmmu_ss.add(when: 'CONFIG_ARMSSE_CPUID', if_true: files('armsse-cpuid.c')) -softmmu_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) +system_ss.add(when: 'CONFIG_TZ_MPC', if_true: files('tz-mpc.c')) +system_ss.add(when: 'CONFIG_TZ_MSC', if_true: files('tz-msc.c')) +system_ss.add(when: 'CONFIG_TZ_PPC', if_true: files('tz-ppc.c')) +system_ss.add(when: 'CONFIG_IOTKIT_SECCTL', if_true: files('iotkit-secctl.c')) +system_ss.add(when: 'CONFIG_IOTKIT_SYSCTL', if_true: files('iotkit-sysctl.c')) +system_ss.add(when: 'CONFIG_IOTKIT_SYSINFO', if_true: files('iotkit-sysinfo.c')) +system_ss.add(when: 'CONFIG_ARMSSE_CPU_PWRCTRL', if_true: files('armsse-cpu-pwrctrl.c')) +system_ss.add(when: 'CONFIG_ARMSSE_CPUID', if_true: files('armsse-cpuid.c')) +system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) -softmmu_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) -softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) -softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( +system_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) +system_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) +system_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_hace.c', 'aspeed_i3c.c', 'aspeed_lpc.c', @@ -126,12 +126,12 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_xdma.c', 'aspeed_peci.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) -softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) +system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) -softmmu_ss.add(when: 'CONFIG_I2C', if_true: files('i2c-echo.c')) +system_ss.add(when: 'CONFIG_I2C', if_true: files('i2c-echo.c')) specific_ss.add(when: 'CONFIG_AVR_POWER', if_true: files('avr_power.c')) @@ -140,7 +140,7 @@ specific_ss.add(when: 'CONFIG_MAC_VIA', if_true: files('mac_via.c')) specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_cmgcr.c', 'mips_cpc.c')) specific_ss.add(when: 'CONFIG_MIPS_ITU', if_true: files('mips_itu.c')) -softmmu_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) +system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) # HPPA devices -softmmu_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) +system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build index 8d85201cb0..7382344628 100644 --- a/hw/net/can/meson.build +++ b/hw/net/can/meson.build @@ -1,8 +1,8 @@ -softmmu_ss.add(when: 'CONFIG_CAN_SJA1000', if_true: files('can_sja1000.c')) -softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_kvaser_pci.c')) -softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_pcm3680_pci.c')) -softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c')) -softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c')) -softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c')) +system_ss.add(when: 'CONFIG_CAN_SJA1000', if_true: files('can_sja1000.c')) +system_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_kvaser_pci.c')) +system_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_pcm3680_pci.c')) +system_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c')) +system_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c')) +system_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c')) diff --git a/hw/net/meson.build b/hw/net/meson.build index a7860c5efe..2632634df3 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -1,75 +1,75 @@ -softmmu_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c')) -softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen_nic.c')) -softmmu_ss.add(when: 'CONFIG_NE2000_COMMON', if_true: files('ne2000.c')) +system_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c')) +system_ss.add(when: 'CONFIG_XEN', if_true: files('xen_nic.c')) +system_ss.add(when: 'CONFIG_NE2000_COMMON', if_true: files('ne2000.c')) # PCI network cards -softmmu_ss.add(when: 'CONFIG_NE2000_PCI', if_true: files('ne2000-pci.c')) -softmmu_ss.add(when: 'CONFIG_EEPRO100_PCI', if_true: files('eepro100.c')) -softmmu_ss.add(when: 'CONFIG_PCNET_PCI', if_true: files('pcnet-pci.c')) -softmmu_ss.add(when: 'CONFIG_PCNET_COMMON', if_true: files('pcnet.c')) -softmmu_ss.add(when: 'CONFIG_E1000_PCI', if_true: files('e1000.c', 'e1000x_common.c')) -softmmu_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -softmmu_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c')) -softmmu_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -softmmu_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 'igbvf.c', 'igb_core.c')) -softmmu_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c')) -softmmu_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c')) -softmmu_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -softmmu_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) +system_ss.add(when: 'CONFIG_NE2000_PCI', if_true: files('ne2000-pci.c')) +system_ss.add(when: 'CONFIG_EEPRO100_PCI', if_true: files('eepro100.c')) +system_ss.add(when: 'CONFIG_PCNET_PCI', if_true: files('pcnet-pci.c')) +system_ss.add(when: 'CONFIG_PCNET_COMMON', if_true: files('pcnet.c')) +system_ss.add(when: 'CONFIG_E1000_PCI', if_true: files('e1000.c', 'e1000x_common.c')) +system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c')) +system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 'igbvf.c', 'igb_core.c')) +system_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c')) +system_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c')) +system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) -softmmu_ss.add(when: 'CONFIG_SMC91C111', if_true: files('smc91c111.c')) -softmmu_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) -softmmu_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) -softmmu_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) -softmmu_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) -softmmu_ss.add(when: 'CONFIG_MIPSNET', if_true: files('mipsnet.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) -softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c')) -softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c')) +system_ss.add(when: 'CONFIG_SMC91C111', if_true: files('smc91c111.c')) +system_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) +system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) +system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) +system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) +system_ss.add(when: 'CONFIG_MIPSNET', if_true: files('mipsnet.c')) +system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) +system_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c')) +system_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c')) -softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c')) -softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c')) -softmmu_ss.add(when: 'CONFIG_LANCE', if_true: files('lance.c')) -softmmu_ss.add(when: 'CONFIG_LASI_I82596', if_true: files('lasi_i82596.c')) -softmmu_ss.add(when: 'CONFIG_I82596_COMMON', if_true: files('i82596.c')) -softmmu_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c')) -softmmu_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c')) -softmmu_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c')) +system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c')) +system_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c')) +system_ss.add(when: 'CONFIG_LANCE', if_true: files('lance.c')) +system_ss.add(when: 'CONFIG_LASI_I82596', if_true: files('lasi_i82596.c')) +system_ss.add(when: 'CONFIG_I82596_COMMON', if_true: files('i82596.c')) +system_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c')) +system_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c')) +system_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_eth.c')) -softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_eth.c')) +system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_llan.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_ETHLITE', if_true: files('xilinx_ethlite.c')) +system_ss.add(when: 'CONFIG_XILINX_ETHLITE', if_true: files('xilinx_ethlite.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('net_rx_pkt.c')) specific_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('virtio-net.c')) if have_vhost_net - softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost_net.c'), if_false: files('vhost_net-stub.c')) - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost_net-stub.c')) + system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost_net.c'), if_false: files('vhost_net-stub.c')) + system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost_net-stub.c')) else - softmmu_ss.add(files('vhost_net-stub.c')) + system_ss.add(files('vhost_net-stub.c')) endif -softmmu_ss.add(when: 'CONFIG_ETSEC', if_true: files( +system_ss.add(when: 'CONFIG_ETSEC', if_true: files( 'fsl_etsec/etsec.c', 'fsl_etsec/miim.c', 'fsl_etsec/registers.c', 'fsl_etsec/rings.c', )) -softmmu_ss.add(when: 'CONFIG_ROCKER', if_true: files( +system_ss.add(when: 'CONFIG_ROCKER', if_true: files( 'rocker/rocker.c', 'rocker/rocker_desc.c', 'rocker/rocker_fp.c', 'rocker/rocker_of_dpa.c', 'rocker/rocker_world.c', ), if_false: files('rocker/qmp-norocker.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c')) -softmmu_ss.add(files('rocker/rocker-hmp-cmds.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c')) +system_ss.add(files('rocker/rocker-hmp-cmds.c')) subdir('can') diff --git a/hw/nubus/meson.build b/hw/nubus/meson.build index 9287c633aa..e7ebda8993 100644 --- a/hw/nubus/meson.build +++ b/hw/nubus/meson.build @@ -4,4 +4,4 @@ nubus_ss.add(files('nubus-bus.c')) nubus_ss.add(files('nubus-bridge.c')) nubus_ss.add(when: 'CONFIG_Q800', if_true: files('mac-nubus-bridge.c')) -softmmu_ss.add_all(when: 'CONFIG_NUBUS', if_true: nubus_ss) +system_ss.add_all(when: 'CONFIG_NUBUS', if_true: nubus_ss) diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build index 3cf40046ee..1a6a2ca2f3 100644 --- a/hw/nvme/meson.build +++ b/hw/nvme/meson.build @@ -1 +1 @@ -softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c')) +system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c')) diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index f5ee9f6b88..988dff6f8e 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -3,21 +3,21 @@ if have_system or have_tools qom_ss.add(files('fw_cfg-interface.c')) endif -softmmu_ss.add(files('fw_cfg.c')) -softmmu_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c')) -softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c')) -softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c')) -softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c')) -softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_CRC', if_true: files('xlnx-efuse-crc.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( +system_ss.add(files('fw_cfg.c')) +system_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c')) +system_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c')) +system_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c')) +system_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c')) +system_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c')) +system_ss.add(when: 'CONFIG_XLNX_EFUSE_CRC', if_true: files('xlnx-efuse-crc.c')) +system_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) +system_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( 'xlnx-versal-efuse-cache.c', 'xlnx-versal-efuse-ctrl.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( +system_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( 'xlnx-zynqmp-efuse.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_BBRAM', if_true: files('xlnx-bbram.c')) +system_ss.add(when: 'CONFIG_XLNX_BBRAM', if_true: files('xlnx-bbram.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build index 0edc6c7cbf..6d5ad9f37b 100644 --- a/hw/pci-bridge/meson.build +++ b/hw/pci-bridge/meson.build @@ -12,6 +12,6 @@ pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c', 'cxl_upstream.c # Sun4u pci_ss.add(when: 'CONFIG_SIMBA', if_true: files('simba.c')) -softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) +system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('pci_expander_bridge_stubs.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('pci_expander_bridge_stubs.c')) diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 9a813d552e..64eada76fe 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -29,7 +29,7 @@ pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) # HPPA devices pci_ss.add(when: 'CONFIG_DINO', if_true: files('dino.c')) -softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) +system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) specific_ss.add(when: 'CONFIG_PCI_POWERNV', if_true: files( 'pnv_phb3.c', diff --git a/hw/pci/meson.build b/hw/pci/meson.build index 4fcd888b27..b1855452f5 100644 --- a/hw/pci/meson.build +++ b/hw/pci/meson.build @@ -16,8 +16,8 @@ pci_ss.add(files( # CONFIG_PCI_EXPRESS=n. pci_ss.add(files('pcie.c', 'pcie_aer.c')) pci_ss.add(files('pcie_doe.c')) -softmmu_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c')) -softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) +system_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c')) +system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) -softmmu_ss.add(when: 'CONFIG_PCI', if_false: files('pci-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('pci-stub.c')) +system_ss.add(when: 'CONFIG_PCI', if_false: files('pci-stub.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('pci-stub.c')) diff --git a/hw/pcmcia/meson.build b/hw/pcmcia/meson.build index 51f2512b8e..04e29c109c 100644 --- a/hw/pcmcia/meson.build +++ b/hw/pcmcia/meson.build @@ -1,2 +1,2 @@ -softmmu_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c')) +system_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c')) diff --git a/hw/rdma/meson.build b/hw/rdma/meson.build index fc7917192f..363c9b8c83 100644 --- a/hw/rdma/meson.build +++ b/hw/rdma/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( +system_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( 'rdma.c', 'rdma_backend.c', 'rdma_utils.c', diff --git a/hw/remote/meson.build b/hw/remote/meson.build index ab25c04906..a1e8708c73 100644 --- a/hw/remote/meson.build +++ b/hw/remote/meson.build @@ -14,4 +14,4 @@ remote_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_true: libvfio_user_dep) specific_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('memory.c')) specific_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('proxy-memory-listener.c')) -softmmu_ss.add_all(when: 'CONFIG_MULTIPROCESS', if_true: remote_ss) +system_ss.add_all(when: 'CONFIG_MULTIPROCESS', if_true: remote_ss) diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index 34a4d316fa..3ea2affe0b 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -1,16 +1,16 @@ -softmmu_ss.add(when: 'CONFIG_DS1338', if_true: files('ds1338.c')) -softmmu_ss.add(when: 'CONFIG_M41T80', if_true: files('m41t80.c')) -softmmu_ss.add(when: 'CONFIG_M48T59', if_true: files('m48t59.c')) -softmmu_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) -softmmu_ss.add(when: 'CONFIG_TWL92230', if_true: files('twl92230.c')) -softmmu_ss.add(when: ['CONFIG_ISA_BUS', 'CONFIG_M48T59'], if_true: files('m48t59-isa.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-rtc.c')) +system_ss.add(when: 'CONFIG_DS1338', if_true: files('ds1338.c')) +system_ss.add(when: 'CONFIG_M41T80', if_true: files('m41t80.c')) +system_ss.add(when: 'CONFIG_M48T59', if_true: files('m48t59.c')) +system_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) +system_ss.add(when: 'CONFIG_TWL92230', if_true: files('twl92230.c')) +system_ss.add(when: ['CONFIG_ISA_BUS', 'CONFIG_M48T59'], if_true: files('m48t59-isa.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-rtc.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_rtc.c')) -softmmu_ss.add(when: 'CONFIG_SUN4V_RTC', if_true: files('sun4v-rtc.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_rtc.c')) -softmmu_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c')) -softmmu_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c')) -softmmu_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_rtc.c')) +system_ss.add(when: 'CONFIG_SUN4V_RTC', if_true: files('sun4v-rtc.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_rtc.c')) +system_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c')) +system_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c')) +system_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c')) diff --git a/hw/scsi/meson.build b/hw/scsi/meson.build index 923a34f344..7a1e7f13f0 100644 --- a/hw/scsi/meson.build +++ b/hw/scsi/meson.build @@ -11,7 +11,7 @@ scsi_ss.add(when: 'CONFIG_LSI_SCSI_PCI', if_true: files('lsi53c895a.c')) scsi_ss.add(when: 'CONFIG_MEGASAS_SCSI_PCI', if_true: files('megasas.c')) scsi_ss.add(when: 'CONFIG_MPTSAS_SCSI_PCI', if_true: files('mptsas.c', 'mptconfig.c', 'mptendian.c')) scsi_ss.add(when: 'CONFIG_VMW_PVSCSI_SCSI_PCI', if_true: files('vmw_pvscsi.c')) -softmmu_ss.add_all(when: 'CONFIG_SCSI', if_true: scsi_ss) +system_ss.add_all(when: 'CONFIG_SCSI', if_true: scsi_ss) specific_scsi_ss = ss.source_set() diff --git a/hw/sd/meson.build b/hw/sd/meson.build index 807ca07b7c..abfac9e461 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -1,13 +1,13 @@ -softmmu_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c')) -softmmu_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c')) -softmmu_ss.add(when: 'CONFIG_SDHCI', if_true: files('sdhci.c')) -softmmu_ss.add(when: 'CONFIG_SDHCI_PCI', if_true: files('sdhci-pci.c')) -softmmu_ss.add(when: 'CONFIG_SSI_SD', if_true: files('ssi-sd.c')) +system_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c')) +system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c')) +system_ss.add(when: 'CONFIG_SDHCI', if_true: files('sdhci.c')) +system_ss.add(when: 'CONFIG_SDHCI_PCI', if_true: files('sdhci-pci.c')) +system_ss.add(when: 'CONFIG_SSI_SD', if_true: files('ssi-sd.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_mmc.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) -softmmu_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_mmc.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) +system_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 9e9be602c3..30e20e27b8 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -1,9 +1,9 @@ -softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) -softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) -softmmu_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) -softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) -softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) -softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) -softmmu_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) -softmmu_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c')) -softmmu_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c')) +system_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) +system_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) +system_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) +system_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) +system_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) +system_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) +system_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) +system_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c')) +system_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c')) diff --git a/hw/smbios/meson.build b/hw/smbios/meson.build index 9e762c7108..6eeae4b35c 100644 --- a/hw/smbios/meson.build +++ b/hw/smbios/meson.build @@ -4,10 +4,10 @@ smbios_ss.add(when: 'CONFIG_IPMI', if_true: files('smbios_type_38.c'), if_false: files('smbios_type_38-stub.c')) -softmmu_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss) -softmmu_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c')) +system_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss) +system_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files( +system_ss.add(when: 'CONFIG_ALL', if_true: files( 'smbios-stub.c', 'smbios_type_38-stub.c', )) diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 904a47161a..0aebcdd614 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -1,13 +1,13 @@ -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c', 'npcm_pspi.c')) -softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) -softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) -softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c', 'npcm_pspi.c')) +system_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) +system_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) +system_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) +system_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) +system_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) +system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 03092e2ceb..fc632ad445 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -1,40 +1,40 @@ -softmmu_ss.add(when: 'CONFIG_A9_GTIMER', if_true: files('a9gtimer.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_PIT', if_true: files('allwinner-a10-pit.c')) -softmmu_ss.add(when: 'CONFIG_ALTERA_TIMER', if_true: files('altera_timer.c')) -softmmu_ss.add(when: 'CONFIG_ARM_MPTIMER', if_true: files('arm_mptimer.c')) -softmmu_ss.add(when: 'CONFIG_ARM_TIMER', if_true: files('arm_timer.c')) -softmmu_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_systick.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_timer.c')) -softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c')) -softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) -softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) -softmmu_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) -softmmu_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) -softmmu_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c')) -softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c')) -softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c')) -softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) -softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) -softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) -softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) +system_ss.add(when: 'CONFIG_A9_GTIMER', if_true: files('a9gtimer.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIT', if_true: files('allwinner-a10-pit.c')) +system_ss.add(when: 'CONFIG_ALTERA_TIMER', if_true: files('altera_timer.c')) +system_ss.add(when: 'CONFIG_ARM_MPTIMER', if_true: files('arm_mptimer.c')) +system_ss.add(when: 'CONFIG_ARM_TIMER', if_true: files('arm_timer.c')) +system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_systick.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_timer.c')) +system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c')) +system_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) +system_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c')) +system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) +system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) +system_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) +system_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) +system_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c')) +system_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c')) +system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c')) +system_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) +system_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) +system_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) +system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) +system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) diff --git a/hw/tpm/meson.build b/hw/tpm/meson.build index 76fe3cb098..6968e60b3f 100644 --- a/hw/tpm/meson.build +++ b/hw/tpm/meson.build @@ -1,9 +1,9 @@ -softmmu_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_tis_common.c')) -softmmu_ss.add(when: 'CONFIG_TPM_TIS_ISA', if_true: files('tpm_tis_isa.c')) -softmmu_ss.add(when: 'CONFIG_TPM_TIS_SYSBUS', if_true: files('tpm_tis_sysbus.c')) -softmmu_ss.add(when: 'CONFIG_TPM_TIS_I2C', if_true: files('tpm_tis_i2c.c')) -softmmu_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_crb.c')) -softmmu_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_ppi.c')) -softmmu_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_ppi.c')) +system_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_tis_common.c')) +system_ss.add(when: 'CONFIG_TPM_TIS_ISA', if_true: files('tpm_tis_isa.c')) +system_ss.add(when: 'CONFIG_TPM_TIS_SYSBUS', if_true: files('tpm_tis_sysbus.c')) +system_ss.add(when: 'CONFIG_TPM_TIS_I2C', if_true: files('tpm_tis_i2c.c')) +system_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_crb.c')) +system_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_ppi.c')) +system_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_ppi.c')) specific_ss.add(when: 'CONFIG_TPM_SPAPR', if_true: files('tpm_spapr.c')) diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 599dc24f0d..e94149ebde 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -1,7 +1,7 @@ hw_usb_modules = {} # usb subsystem core -softmmu_ss.add(when: 'CONFIG_USB', if_true: files( +system_ss.add(when: 'CONFIG_USB', if_true: files( 'bus.c', 'combined-packet.c', 'core.c', @@ -12,42 +12,42 @@ softmmu_ss.add(when: 'CONFIG_USB', if_true: files( )) # usb host adapters -softmmu_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c')) -softmmu_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c')) -softmmu_ss.add(when: 'CONFIG_USB_OHCI_PCI', if_true: files('hcd-ohci-pci.c')) -softmmu_ss.add(when: 'CONFIG_USB_EHCI', if_true: files('hcd-ehci.c')) -softmmu_ss.add(when: 'CONFIG_USB_EHCI_PCI', if_true: files('hcd-ehci-pci.c')) -softmmu_ss.add(when: 'CONFIG_USB_EHCI_SYSBUS', if_true: files('hcd-ehci.c', 'hcd-ehci-sysbus.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c')) -softmmu_ss.add(when: 'CONFIG_USB_MUSB', if_true: files('hcd-musb.c')) -softmmu_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c')) -softmmu_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) +system_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c')) +system_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c')) +system_ss.add(when: 'CONFIG_USB_OHCI_PCI', if_true: files('hcd-ohci-pci.c')) +system_ss.add(when: 'CONFIG_USB_EHCI', if_true: files('hcd-ehci.c')) +system_ss.add(when: 'CONFIG_USB_EHCI_PCI', if_true: files('hcd-ehci-pci.c')) +system_ss.add(when: 'CONFIG_USB_EHCI_SYSBUS', if_true: files('hcd-ehci.c', 'hcd-ehci-sysbus.c')) +system_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c')) +system_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c')) +system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c')) +system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c')) +system_ss.add(when: 'CONFIG_USB_MUSB', if_true: files('hcd-musb.c')) +system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c')) +system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) -softmmu_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) -softmmu_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) -softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) +system_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) +system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) +system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) +system_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) # emulated usb devices -softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) -softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) -softmmu_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c')) -softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c')) -softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c')) -softmmu_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c')) -softmmu_ss.add(when: ['CONFIG_POSIX', 'CONFIG_USB_STORAGE_MTP'], if_true: files('dev-mtp.c')) +system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) +system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) +system_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c')) +system_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c')) +system_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c')) +system_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c')) +system_ss.add(when: ['CONFIG_POSIX', 'CONFIG_USB_STORAGE_MTP'], if_true: files('dev-mtp.c')) # smartcard -softmmu_ss.add(when: 'CONFIG_USB_SMARTCARD', if_true: files('dev-smartcard-reader.c')) +system_ss.add(when: 'CONFIG_USB_SMARTCARD', if_true: files('dev-smartcard-reader.c')) if cacard.found() usbsmartcard_ss = ss.source_set() @@ -57,15 +57,15 @@ if cacard.found() endif # U2F -softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: files('u2f.c')) -softmmu_ss.add(when: ['CONFIG_LINUX', 'CONFIG_USB_U2F'], if_true: [libudev, files('u2f-passthru.c')]) +system_ss.add(when: 'CONFIG_USB_U2F', if_true: files('u2f.c')) +system_ss.add(when: ['CONFIG_LINUX', 'CONFIG_USB_U2F'], if_true: [libudev, files('u2f-passthru.c')]) if u2f.found() - softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')]) + system_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')]) endif # CanoKey if canokey.found() - softmmu_ss.add(when: 'CONFIG_USB_CANOKEY', if_true: [canokey, files('canokey.c')]) + system_ss.add(when: 'CONFIG_USB_CANOKEY', if_true: [canokey, files('canokey.c')]) endif # usb redirect @@ -84,6 +84,6 @@ if libusb.found() hw_usb_modules += {'host': usbhost_ss} endif -softmmu_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN_BUS', libusb], if_true: files('xen-usb.c')) +system_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN_BUS', libusb], if_true: files('xen-usb.c')) modules += { 'hw-usb': hw_usb_modules } diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index bdec78bfc6..e83c37fffd 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -62,11 +62,11 @@ virtio_pci_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev-pci.c' specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) -softmmu_ss.add_all(when: 'CONFIG_VIRTIO', if_true: softmmu_virtio_ss) -softmmu_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) -softmmu_ss.add(files('virtio-hmp-cmds.c')) +system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: softmmu_virtio_ss) +system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) +system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) +system_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) +system_ss.add(files('virtio-hmp-cmds.c')) specific_ss.add_all(when: 'CONFIG_VIRTIO', if_true: specific_virtio_ss) diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 5dcd4fbe2f..15370565bd 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -1,10 +1,10 @@ -softmmu_ss.add(files('watchdog.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_WDT', if_true: files('allwinner-wdt.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_WATCHDOG', if_true: files('cmsdk-apb-watchdog.c')) -softmmu_ss.add(when: 'CONFIG_WDT_IB6300ESB', if_true: files('wdt_i6300esb.c')) -softmmu_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c')) -softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) -softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) -softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) +system_ss.add(files('watchdog.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_WDT', if_true: files('allwinner-wdt.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_WATCHDOG', if_true: files('cmsdk-apb-watchdog.c')) +system_ss.add(when: 'CONFIG_WDT_IB6300ESB', if_true: files('wdt_i6300esb.c')) +system_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c')) +system_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) +system_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) +system_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c')) diff --git a/hw/xen/meson.build b/hw/xen/meson.build index afd20754a1..277f9f292b 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files( +system_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files( 'xen-backend.c', 'xen-bus-helper.c', 'xen-bus.c', @@ -7,7 +7,7 @@ softmmu_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files( 'xen_pvdev.c', )) -softmmu_ss.add(when: ['CONFIG_XEN', xen], if_true: files( +system_ss.add(when: ['CONFIG_XEN', xen], if_true: files( 'xen-operations.c', )) diff --git a/meson.build b/meson.build index 0faca1dc0d..6ef78ea278 100644 --- a/meson.build +++ b/meson.build @@ -3171,7 +3171,7 @@ hwcore_ss = ss.source_set() io_ss = ss.source_set() qmp_ss = ss.source_set() qom_ss = ss.source_set() -softmmu_ss = ss.source_set() +system_ss = ss.source_set() specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() stub_ss = ss.source_set() @@ -3396,7 +3396,7 @@ if have_block # os-posix.c contains POSIX-specific functions used by qemu-storage-daemon, # os-win32.c does not blockdev_ss.add(when: 'CONFIG_POSIX', if_true: files('os-posix.c')) - softmmu_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')]) + system_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')]) endif common_ss.add(files('cpus-common.c')) @@ -3496,7 +3496,7 @@ foreach d, list : modules if d == 'block' block_ss.add_all(module_ss) else - softmmu_ss.add_all(module_ss) + system_ss.add_all(module_ss) endif endif endforeach @@ -3601,7 +3601,7 @@ libmigration = static_library('migration', sources: migration_files + genh, build_by_default: false) migration = declare_dependency(link_with: libmigration, dependencies: [zlib, qom, io]) -softmmu_ss.add(migration) +system_ss.add(migration) block_ss = block_ss.apply(config_host, strict: false) libblock = static_library('block', block_ss.sources() + genh, @@ -3662,10 +3662,10 @@ if emulator_modules.length() > 0 alias_target('modules', emulator_modules) endif -softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp) +system_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) -common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [softmmu_ss]) +common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss]) common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) common_all = common_ss.apply(config_all, strict: false) diff --git a/migration/meson.build b/migration/meson.build index 9975407cd0..1ae28523a1 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -9,7 +9,7 @@ migration_files = files( 'yank_functions.c', ) -softmmu_ss.add(files( +system_ss.add(files( 'block-dirty-bitmap.c', 'channel.c', 'channel-block.c', @@ -31,14 +31,14 @@ softmmu_ss.add(files( ), gnutls) if get_option('replication').allowed() - softmmu_ss.add(files('colo-failover.c', 'colo.c')) + system_ss.add(files('colo-failover.c', 'colo.c')) endif -softmmu_ss.add(when: rdma, if_true: files('rdma.c')) +system_ss.add(when: rdma, if_true: files('rdma.c')) if get_option('live_block_migration').allowed() - softmmu_ss.add(files('block.c')) + system_ss.add(files('block.c')) endif -softmmu_ss.add(when: zstd, if_true: files('multifd-zstd.c')) +system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('ram.c', diff --git a/monitor/meson.build b/monitor/meson.build index 4c0a33ae65..5269492cf0 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -1,11 +1,11 @@ qmp_ss.add(files('monitor.c', 'qmp.c', 'qmp-cmds-control.c')) -softmmu_ss.add(files( +system_ss.add(files( 'fds.c', 'hmp-cmds.c', 'hmp.c', )) -softmmu_ss.add([spice_headers, files('qmp-cmds.c')]) +system_ss.add([spice_headers, files('qmp-cmds.c')]) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'hmp-cmds-target.c', 'hmp-target.c'), spice]) diff --git a/net/can/meson.build b/net/can/meson.build index f53d9ec54f..45693c82c9 100644 --- a/net/can/meson.build +++ b/net/can/meson.build @@ -2,4 +2,4 @@ can_ss = ss.source_set() can_ss.add(files('can_core.c', 'can_host.c')) can_ss.add(when: 'CONFIG_LINUX', if_true: files('can_socketcan.c')) -softmmu_ss.add_all(when: 'CONFIG_CAN_BUS', if_true: can_ss) +system_ss.add_all(when: 'CONFIG_CAN_BUS', if_true: can_ss) diff --git a/net/meson.build b/net/meson.build index 6f4ecde57f..bdf564a57b 100644 --- a/net/meson.build +++ b/net/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(files( +system_ss.add(files( 'announce.c', 'checksum.c', 'dump.c', @@ -18,41 +18,41 @@ softmmu_ss.add(files( if get_option('replication').allowed() or \ get_option('colo_proxy').allowed() - softmmu_ss.add(files('colo-compare.c')) - softmmu_ss.add(files('colo.c')) + system_ss.add(files('colo-compare.c')) + system_ss.add(files('colo.c')) endif if get_option('colo_proxy').allowed() - softmmu_ss.add(files('filter-rewriter.c')) + system_ss.add(files('filter-rewriter.c')) endif -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) +system_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) if have_l2tpv3 - softmmu_ss.add(files('l2tpv3.c')) + system_ss.add(files('l2tpv3.c')) endif -softmmu_ss.add(when: slirp, if_true: files('slirp.c')) -softmmu_ss.add(when: vde, if_true: files('vde.c')) +system_ss.add(when: slirp, if_true: files('slirp.c')) +system_ss.add(when: vde, if_true: files('vde.c')) if have_netmap - softmmu_ss.add(files('netmap.c')) + system_ss.add(files('netmap.c')) endif if have_vhost_net_user - softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-user.c'), if_false: files('vhost-user-stub.c')) - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-user-stub.c')) + system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-user.c'), if_false: files('vhost-user-stub.c')) + system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-user-stub.c')) endif -softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('tap-linux.c')) -softmmu_ss.add(when: 'CONFIG_BSD', if_true: files('tap-bsd.c')) -softmmu_ss.add(when: 'CONFIG_SOLARIS', if_true: files('tap-solaris.c')) +system_ss.add(when: 'CONFIG_LINUX', if_true: files('tap-linux.c')) +system_ss.add(when: 'CONFIG_BSD', if_true: files('tap-bsd.c')) +system_ss.add(when: 'CONFIG_SOLARIS', if_true: files('tap-solaris.c')) tap_posix = ['tap.c'] if not config_host.has_key('CONFIG_LINUX') and not config_host.has_key('CONFIG_BSD') and not config_host.has_key('CONFIG_SOLARIS') tap_posix += 'tap-stub.c' endif -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files(tap_posix)) -softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('tap-win32.c')) +system_ss.add(when: 'CONFIG_POSIX', if_true: files(tap_posix)) +system_ss.add(when: 'CONFIG_WIN32', if_true: files('tap-win32.c')) if have_vhost_net_vdpa - softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c')) - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-vdpa-stub.c')) + system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c')) + system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-vdpa-stub.c')) endif vmnet_files = files( @@ -61,5 +61,5 @@ vmnet_files = files( 'vmnet-host.c', 'vmnet-shared.c' ) -softmmu_ss.add(when: vmnet, if_true: vmnet_files) +system_ss.add(when: vmnet, if_true: vmnet_files) subdir('can') diff --git a/qom/meson.build b/qom/meson.build index 062a3789d8..8192243430 100644 --- a/qom/meson.build +++ b/qom/meson.build @@ -7,4 +7,4 @@ qom_ss.add(files( )) qmp_ss.add(files('qom-qmp-cmds.c')) -softmmu_ss.add(files('qom-hmp-cmds.c')) +system_ss.add(files('qom-hmp-cmds.c')) diff --git a/replay/meson.build b/replay/meson.build index 21aefad220..4b4175e8dd 100644 --- a/replay/meson.build +++ b/replay/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files( +system_ss.add(when: 'CONFIG_TCG', if_true: files( 'replay.c', 'replay-internal.c', 'replay-events.c', diff --git a/softmmu/meson.build b/softmmu/meson.build index d75f45b7c3..ea5603f021 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -10,7 +10,7 @@ specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: [files( 'icount.c', )]) -softmmu_ss.add(files( +system_ss.add(files( 'balloon.c', 'bootdevice.c', 'cpus.c', @@ -32,8 +32,8 @@ softmmu_ss.add(files( ), sdl, libpmem, libdaxctl) if have_tpm - softmmu_ss.add(files('tpm.c')) + system_ss.add(files('tpm.c')) endif -softmmu_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) -softmmu_ss.add(when: fdt, if_true: files('device_tree.c')) +system_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) +system_ss.add(when: fdt, if_true: files('device_tree.c')) diff --git a/stats/meson.build b/stats/meson.build index c4153f979e..0728dafcd1 100644 --- a/stats/meson.build +++ b/stats/meson.build @@ -1 +1 @@ -softmmu_ss.add(files('stats-hmp-cmds.c', 'stats-qmp-cmds.c')) +system_ss.add(files('stats-hmp-cmds.c', 'stats-qmp-cmds.c')) diff --git a/target/alpha/meson.build b/target/alpha/meson.build index 1aec55abb4..3f5253c002 100644 --- a/target/alpha/meson.build +++ b/target/alpha/meson.build @@ -11,8 +11,8 @@ alpha_ss.add(files( 'vax_helper.c', )) -alpha_softmmu_ss = ss.source_set() -alpha_softmmu_ss.add(files('machine.c')) +alpha_system_ss = ss.source_set() +alpha_system_ss.add(files('machine.c')) target_arch += {'alpha': alpha_ss} -target_softmmu_arch += {'alpha': alpha_softmmu_ss} +target_softmmu_arch += {'alpha': alpha_system_ss} diff --git a/target/arm/hvf/meson.build b/target/arm/hvf/meson.build index 855e6cce5a..afc509a470 100644 --- a/target/arm/hvf/meson.build +++ b/target/arm/hvf/meson.build @@ -1,3 +1,3 @@ -arm_softmmu_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( +arm_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', )) diff --git a/target/arm/meson.build b/target/arm/meson.build index 011e8ca113..e645e456da 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -16,8 +16,8 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'gdbstub64.c', )) -arm_softmmu_ss = ss.source_set() -arm_softmmu_ss.add(files( +arm_system_ss = ss.source_set() +arm_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', 'arm-qmp-cmds.c', @@ -35,4 +35,4 @@ else endif target_arch += {'arm': arm_ss} -target_softmmu_arch += {'arm': arm_softmmu_ss} +target_softmmu_arch += {'arm': arm_system_ss} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 130ed62fcd..bdcab56489 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -48,6 +48,6 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'sve_helper.c', )) -arm_softmmu_ss.add(files( +arm_system_ss.add(files( 'psci.c', )) diff --git a/target/avr/meson.build b/target/avr/meson.build index 7e8e29c59d..a24cf6d26d 100644 --- a/target/avr/meson.build +++ b/target/avr/meson.build @@ -4,7 +4,7 @@ gen = [ ] avr_ss = ss.source_set() -avr_softmmu_ss = ss.source_set() +avr_system_ss = ss.source_set() avr_ss.add(gen) avr_ss.add(files( @@ -14,7 +14,7 @@ avr_ss.add(files( 'gdbstub.c', 'disas.c')) -avr_softmmu_ss.add(files('machine.c')) +avr_system_ss.add(files('machine.c')) target_arch += {'avr': avr_ss} -target_softmmu_arch += {'avr': avr_softmmu_ss} +target_softmmu_arch += {'avr': avr_system_ss} diff --git a/target/cris/meson.build b/target/cris/meson.build index c1e326d950..07dc3a5682 100644 --- a/target/cris/meson.build +++ b/target/cris/meson.build @@ -6,12 +6,12 @@ cris_ss.add(files( 'translate.c', )) -cris_softmmu_ss = ss.source_set() -cris_softmmu_ss.add(files( +cris_system_ss = ss.source_set() +cris_system_ss.add(files( 'helper.c', 'machine.c', 'mmu.c', )) target_arch += {'cris': cris_ss} -target_softmmu_arch += {'cris': cris_softmmu_ss} +target_softmmu_arch += {'cris': cris_system_ss} diff --git a/target/hppa/meson.build b/target/hppa/meson.build index 83b1e0ee7d..59b68e82e2 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -11,8 +11,8 @@ hppa_ss.add(files( 'translate.c', )) -hppa_softmmu_ss = ss.source_set() -hppa_softmmu_ss.add(files( +hppa_system_ss = ss.source_set() +hppa_system_ss.add(files( 'int_helper.c', 'machine.c', 'mem_helper.c', @@ -20,4 +20,4 @@ hppa_softmmu_ss.add(files( )) target_arch += {'hppa': hppa_ss} -target_softmmu_arch += {'hppa': hppa_softmmu_ss} +target_softmmu_arch += {'hppa': hppa_system_ss} diff --git a/target/i386/hax/meson.build b/target/i386/hax/meson.build index d6c520fb6b..6ac314aa35 100644 --- a/target/i386/hax/meson.build +++ b/target/i386/hax/meson.build @@ -1,7 +1,7 @@ -i386_softmmu_ss.add(when: 'CONFIG_HAX', if_true: files( +i386_system_ss.add(when: 'CONFIG_HAX', if_true: files( 'hax-all.c', 'hax-mem.c', 'hax-accel-ops.c', )) -i386_softmmu_ss.add(when: ['CONFIG_HAX', 'CONFIG_POSIX'], if_true: files('hax-posix.c')) -i386_softmmu_ss.add(when: ['CONFIG_HAX', 'CONFIG_WIN32'], if_true: files('hax-windows.c')) +i386_system_ss.add(when: ['CONFIG_HAX', 'CONFIG_POSIX'], if_true: files('hax-posix.c')) +i386_system_ss.add(when: ['CONFIG_HAX', 'CONFIG_WIN32'], if_true: files('hax-windows.c')) diff --git a/target/i386/hvf/meson.build b/target/i386/hvf/meson.build index f6d4c394d3..05c3c8cf18 100644 --- a/target/i386/hvf/meson.build +++ b/target/i386/hvf/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( +i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', 'x86.c', 'x86_cpuid.c', diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 322272091b..40fbde96ca 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -11,6 +11,6 @@ i386_softmmu_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) i386_softmmu_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) -i386_softmmu_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) +i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) -i386_softmmu_ss.add_all(when: 'CONFIG_KVM', if_true: i386_softmmu_kvm_ss) +i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_softmmu_kvm_ss) diff --git a/target/i386/meson.build b/target/i386/meson.build index ae38dc9563..1effe1ed9a 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -12,15 +12,15 @@ i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c')) -i386_softmmu_ss = ss.source_set() -i386_softmmu_ss.add(files( +i386_system_ss = ss.source_set() +i386_system_ss.add(files( 'arch_dump.c', 'arch_memory_mapping.c', 'machine.c', 'monitor.c', 'cpu-sysemu.c', )) -i386_softmmu_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) +i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) i386_user_ss = ss.source_set() @@ -32,5 +32,5 @@ subdir('hvf') subdir('tcg') target_arch += {'i386': i386_ss} -target_softmmu_arch += {'i386': i386_softmmu_ss} +target_softmmu_arch += {'i386': i386_system_ss} target_user_arch += {'i386': i386_user_ss} diff --git a/target/i386/nvmm/meson.build b/target/i386/nvmm/meson.build index 733e334083..885a708665 100644 --- a/target/i386/nvmm/meson.build +++ b/target/i386/nvmm/meson.build @@ -1,8 +1,8 @@ -i386_softmmu_ss.add(when: 'CONFIG_NVMM', if_true: +i386_system_ss.add(when: 'CONFIG_NVMM', if_true: files( 'nvmm-all.c', 'nvmm-accel-ops.c', ) ) -i386_softmmu_ss.add(when: 'CONFIG_NVMM', if_true: nvmm) +i386_system_ss.add(when: 'CONFIG_NVMM', if_true: nvmm) diff --git a/target/i386/tcg/sysemu/meson.build b/target/i386/tcg/sysemu/meson.build index 7179bd21fb..f9ac254541 100644 --- a/target/i386/tcg/sysemu/meson.build +++ b/target/i386/tcg/sysemu/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: ['CONFIG_TCG', 'CONFIG_SYSTEM_ONLY'], if_true: files( +i386_system_ss.add(when: ['CONFIG_TCG', 'CONFIG_SYSTEM_ONLY'], if_true: files( 'tcg-cpu.c', 'smm_helper.c', 'excp_helper.c', diff --git a/target/i386/whpx/meson.build b/target/i386/whpx/meson.build index 95fc31eb81..9c54aaad39 100644 --- a/target/i386/whpx/meson.build +++ b/target/i386/whpx/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: 'CONFIG_WHPX', if_true: files( +i386_system_ss.add(when: 'CONFIG_WHPX', if_true: files( 'whpx-all.c', 'whpx-apic.c', 'whpx-accel-ops.c', diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build index 1117a51c52..b7a27df5a9 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -15,8 +15,8 @@ loongarch_tcg_ss.add(files( )) loongarch_tcg_ss.add(zlib) -loongarch_softmmu_ss = ss.source_set() -loongarch_softmmu_ss.add(files( +loongarch_system_ss = ss.source_set() +loongarch_system_ss.add(files( 'loongarch-qmp-cmds.c', 'machine.c', 'tlb_helper.c', @@ -30,4 +30,4 @@ common_ss.add(when: 'CONFIG_LOONGARCH_DIS', if_true: [files('disas.c'), gen]) loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss]) target_arch += {'loongarch': loongarch_ss} -target_softmmu_arch += {'loongarch': loongarch_softmmu_ss} +target_softmmu_arch += {'loongarch': loongarch_system_ss} diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 27d2d7ba87..355db26c6f 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -9,11 +9,11 @@ m68k_ss.add(files( 'translate.c', )) -m68k_softmmu_ss = ss.source_set() -m68k_softmmu_ss.add(files( +m68k_system_ss = ss.source_set() +m68k_system_ss.add(files( 'm68k-semi.c', 'monitor.c' )) target_arch += {'m68k': m68k_ss} -target_softmmu_arch += {'m68k': m68k_softmmu_ss} +target_softmmu_arch += {'m68k': m68k_system_ss} diff --git a/target/microblaze/meson.build b/target/microblaze/meson.build index 05ee0ec163..50fd9ff378 100644 --- a/target/microblaze/meson.build +++ b/target/microblaze/meson.build @@ -10,11 +10,11 @@ microblaze_ss.add(files( 'translate.c', )) -microblaze_softmmu_ss = ss.source_set() -microblaze_softmmu_ss.add(files( +microblaze_system_ss = ss.source_set() +microblaze_system_ss.add(files( 'mmu.c', 'machine.c', )) target_arch += {'microblaze': microblaze_ss} -target_softmmu_arch += {'microblaze': microblaze_softmmu_ss} +target_softmmu_arch += {'microblaze': microblaze_system_ss} diff --git a/target/mips/meson.build b/target/mips/meson.build index 2407a05d4c..f35e8f0eca 100644 --- a/target/mips/meson.build +++ b/target/mips/meson.build @@ -1,5 +1,5 @@ mips_user_ss = ss.source_set() -mips_softmmu_ss = ss.source_set() +mips_system_ss = ss.source_set() mips_ss = ss.source_set() mips_ss.add(files( 'cpu.c', @@ -19,5 +19,5 @@ endif mips_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) target_arch += {'mips': mips_ss} -target_softmmu_arch += {'mips': mips_softmmu_ss} +target_softmmu_arch += {'mips': mips_system_ss} target_user_arch += {'mips': mips_user_ss} diff --git a/target/mips/sysemu/meson.build b/target/mips/sysemu/meson.build index 261492de5b..498cf289d6 100644 --- a/target/mips/sysemu/meson.build +++ b/target/mips/sysemu/meson.build @@ -1,4 +1,4 @@ -mips_softmmu_ss.add(files( +mips_system_ss.add(files( 'addr.c', 'cp0.c', 'cp0_timer.c', diff --git a/target/mips/tcg/sysemu/meson.build b/target/mips/tcg/sysemu/meson.build index 4da2c577b2..43b35b3803 100644 --- a/target/mips/tcg/sysemu/meson.build +++ b/target/mips/tcg/sysemu/meson.build @@ -1,4 +1,4 @@ -mips_softmmu_ss.add(files( +mips_system_ss.add(files( 'cp0_helper.c', 'mips-semi.c', 'special_helper.c', diff --git a/target/nios2/meson.build b/target/nios2/meson.build index c6e2243cc3..8f0f9dc628 100644 --- a/target/nios2/meson.build +++ b/target/nios2/meson.build @@ -5,8 +5,8 @@ nios2_ss.add(files( 'translate.c', )) -nios2_softmmu_ss = ss.source_set() -nios2_softmmu_ss.add(files( +nios2_system_ss = ss.source_set() +nios2_system_ss.add(files( 'helper.c', 'monitor.c', 'mmu.c', @@ -14,4 +14,4 @@ nios2_softmmu_ss.add(files( )) target_arch += {'nios2': nios2_ss} -target_softmmu_arch += {'nios2': nios2_softmmu_ss} +target_softmmu_arch += {'nios2': nios2_system_ss} diff --git a/target/openrisc/meson.build b/target/openrisc/meson.build index 84322086ec..c1cd943f78 100644 --- a/target/openrisc/meson.build +++ b/target/openrisc/meson.build @@ -14,12 +14,12 @@ openrisc_ss.add(files( 'translate.c', )) -openrisc_softmmu_ss = ss.source_set() -openrisc_softmmu_ss.add(files( +openrisc_system_ss = ss.source_set() +openrisc_system_ss.add(files( 'interrupt.c', 'machine.c', 'mmu.c', )) target_arch += {'openrisc': openrisc_ss} -target_softmmu_arch += {'openrisc': openrisc_softmmu_ss} +target_softmmu_arch += {'openrisc': openrisc_system_ss} diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 7929de8360..a69f174f41 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -33,21 +33,21 @@ ppc_ss.add(gen) ppc_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) ppc_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user_only_helper.c')) -ppc_softmmu_ss = ss.source_set() -ppc_softmmu_ss.add(files( +ppc_system_ss = ss.source_set() +ppc_system_ss.add(files( 'arch_dump.c', 'machine.c', 'mmu-hash32.c', 'mmu_common.c', 'ppc-qmp-cmds.c', )) -ppc_softmmu_ss.add(when: 'CONFIG_TCG', if_true: files( +ppc_system_ss.add(when: 'CONFIG_TCG', if_true: files( 'mmu_helper.c', ), if_false: files( 'tcg-stub.c', )) -ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files( +ppc_system_ss.add(when: 'TARGET_PPC64', if_true: files( 'compat.c', 'mmu-book3s-v3.c', 'mmu-hash64.c', @@ -55,4 +55,4 @@ ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files( )) target_arch += {'ppc': ppc_ss} -target_softmmu_arch += {'ppc': ppc_softmmu_ss} +target_softmmu_arch += {'ppc': ppc_system_ss} diff --git a/target/riscv/meson.build b/target/riscv/meson.build index e1ff6d9b95..7f56c5f88d 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -24,8 +24,8 @@ riscv_ss.add(files( )) riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) -riscv_softmmu_ss = ss.source_set() -riscv_softmmu_ss.add(files( +riscv_system_ss = ss.source_set() +riscv_system_ss.add(files( 'arch_dump.c', 'pmp.c', 'debug.c', @@ -37,4 +37,4 @@ riscv_softmmu_ss.add(files( )) target_arch += {'riscv': riscv_ss} -target_softmmu_arch += {'riscv': riscv_softmmu_ss} +target_softmmu_arch += {'riscv': riscv_system_ss} diff --git a/target/s390x/kvm/meson.build b/target/s390x/kvm/meson.build index aef52b6686..37253f75bf 100644 --- a/target/s390x/kvm/meson.build +++ b/target/s390x/kvm/meson.build @@ -14,6 +14,6 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( # - KVM is enabled # - the linker supports --s390-pgste if host_machine.cpu_family() == 's390x' and cc.has_link_argument('-Wl,--s390-pgste') - s390x_softmmu_ss.add(when: 'CONFIG_KVM', + s390x_system_ss.add(when: 'CONFIG_KVM', if_true: declare_dependency(link_args: ['-Wl,--s390-pgste'])) endif diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 84c1402a6a..42ed38942a 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -18,8 +18,8 @@ gen_features_h = custom_target('gen-features.h', s390x_ss.add(gen_features_h) -s390x_softmmu_ss = ss.source_set() -s390x_softmmu_ss.add(files( +s390x_system_ss = ss.source_set() +s390x_system_ss.add(files( 'helper.c', 'arch_dump.c', 'diag.c', @@ -40,5 +40,5 @@ subdir('tcg') subdir('kvm') target_arch += {'s390x': s390x_ss} -target_softmmu_arch += {'s390x': s390x_softmmu_ss} +target_softmmu_arch += {'s390x': s390x_system_ss} target_user_arch += {'s390x': s390x_user_ss} diff --git a/target/sh4/meson.build b/target/sh4/meson.build index 56a57576da..a78e9ec7e4 100644 --- a/target/sh4/meson.build +++ b/target/sh4/meson.build @@ -7,8 +7,8 @@ sh4_ss.add(files( 'translate.c', )) -sh4_softmmu_ss = ss.source_set() -sh4_softmmu_ss.add(files('monitor.c')) +sh4_system_ss = ss.source_set() +sh4_system_ss.add(files('monitor.c')) target_arch += {'sh4': sh4_ss} -target_softmmu_arch += {'sh4': sh4_softmmu_ss} +target_softmmu_arch += {'sh4': sh4_system_ss} diff --git a/target/sparc/meson.build b/target/sparc/meson.build index a801802ee2..d32e67b287 100644 --- a/target/sparc/meson.build +++ b/target/sparc/meson.build @@ -12,12 +12,12 @@ sparc_ss.add(files( sparc_ss.add(when: 'TARGET_SPARC', if_true: files('int32_helper.c')) sparc_ss.add(when: 'TARGET_SPARC64', if_true: files('int64_helper.c', 'vis_helper.c')) -sparc_softmmu_ss = ss.source_set() -sparc_softmmu_ss.add(files( +sparc_system_ss = ss.source_set() +sparc_system_ss.add(files( 'machine.c', 'mmu_helper.c', 'monitor.c', )) target_arch += {'sparc': sparc_ss} -target_softmmu_arch += {'sparc': sparc_softmmu_ss} +target_softmmu_arch += {'sparc': sparc_system_ss} diff --git a/target/tricore/meson.build b/target/tricore/meson.build index 0ccc829517..34825b6048 100644 --- a/target/tricore/meson.build +++ b/target/tricore/meson.build @@ -9,7 +9,7 @@ tricore_ss.add(files( )) tricore_ss.add(zlib) -tricore_softmmu_ss = ss.source_set() +tricore_system_ss = ss.source_set() target_arch += {'tricore': tricore_ss} -target_softmmu_arch += {'tricore': tricore_softmmu_ss} +target_softmmu_arch += {'tricore': tricore_system_ss} diff --git a/target/xtensa/meson.build b/target/xtensa/meson.build index 20bbf9b335..95692bd75f 100644 --- a/target/xtensa/meson.build +++ b/target/xtensa/meson.build @@ -15,8 +15,8 @@ xtensa_ss.add(files( 'xtensa-isa.c', )) -xtensa_softmmu_ss = ss.source_set() -xtensa_softmmu_ss.add(files( +xtensa_system_ss = ss.source_set() +xtensa_system_ss.add(files( 'dbg_helper.c', 'mmu_helper.c', 'monitor.c', @@ -24,4 +24,4 @@ xtensa_softmmu_ss.add(files( )) target_arch += {'xtensa': xtensa_ss} -target_softmmu_arch += {'xtensa': xtensa_softmmu_ss} +target_softmmu_arch += {'xtensa': xtensa_system_ss} diff --git a/tcg/meson.build b/tcg/meson.build index 565c60bc96..c0252c4198 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -42,4 +42,4 @@ libtcg_softmmu = static_library('tcg_softmmu', tcg_softmmu = declare_dependency(link_with: libtcg_softmmu, dependencies: tcg_ss.dependencies()) -softmmu_ss.add(tcg_softmmu) +system_ss.add(tcg_softmmu) diff --git a/trace/meson.build b/trace/meson.build index 8e80be895c..b0d31a67e6 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(files('trace-hmp-cmds.c')) +system_ss.add(files('trace-hmp-cmds.c')) specific_ss.add(files('control-target.c')) diff --git a/ui/meson.build b/ui/meson.build index 125dd41cd6..a5506ac8ad 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -1,9 +1,9 @@ -softmmu_ss.add(pixman) +system_ss.add(pixman) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: pixman) # for the include path specific_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: opengl) # for the include path -softmmu_ss.add(png) -softmmu_ss.add(files( +system_ss.add(png) +system_ss.add(files( 'clipboard.c', 'console.c', 'cursor.c', @@ -19,16 +19,16 @@ softmmu_ss.add(files( 'util.c', )) if dbus_display - softmmu_ss.add(files('dbus-module.c')) + system_ss.add(files('dbus-module.c')) endif -softmmu_ss.add([spice_headers, files('spice-module.c')]) -softmmu_ss.add(when: spice_protocol, if_true: files('vdagent.c')) +system_ss.add([spice_headers, files('spice-module.c')]) +system_ss.add(when: spice_protocol, if_true: files('vdagent.c')) -softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files( +system_ss.add(when: 'CONFIG_LINUX', if_true: files( 'input-linux.c', 'udmabuf.c', )) -softmmu_ss.add(when: cocoa, if_true: files('cocoa.m')) +system_ss.add(when: cocoa, if_true: files('cocoa.m')) vnc_ss = ss.source_set() vnc_ss.add(files( @@ -45,8 +45,8 @@ vnc_ss.add(files( )) vnc_ss.add(zlib, jpeg, gnutls) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) -softmmu_ss.add_all(when: vnc, if_true: vnc_ss) -softmmu_ss.add(when: vnc, if_false: files('vnc-stubs.c')) +system_ss.add_all(when: vnc, if_true: vnc_ss) +system_ss.add(when: vnc, if_false: files('vnc-stubs.c')) ui_modules = {} @@ -56,7 +56,7 @@ if curses.found() ui_modules += {'curses' : curses_ss} endif -softmmu_ss.add(opengl) +system_ss.add(opengl) if opengl.found() opengl_ss = ss.source_set() opengl_ss.add(gbm) @@ -98,7 +98,7 @@ if dbus_display endif if gtk.found() - softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) + system_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) gtk_ss = ss.source_set() gtk_ss.add(gtk, vte, pixman, files('gtk.c')) @@ -112,7 +112,7 @@ if gtk.found() endif if sdl.found() - softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) + system_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) sdl_ss = ss.source_set() sdl_ss.add(sdl, sdl_image, pixman, glib, files( From 69a305eff4bd5f642c7be596e3511f7400f737f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 11 Jun 2023 10:58:21 +0200 Subject: [PATCH 407/412] plugins: Remove unused 'exec/helper-proto.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230611085846.21415-2-philmd@linaro.org> Signed-off-by: Richard Henderson --- plugins/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/core.c b/plugins/core.c index 9912f2cfdb..3c4e26c7ed 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -25,7 +25,6 @@ #include "exec/exec-all.h" #include "exec/tb-flush.h" -#include "exec/helper-proto.h" #include "tcg/tcg.h" #include "tcg/tcg-op.h" #include "plugin.h" From a3e7f7022901c1d7e59ae477295f1a96b79bd3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 11 Jun 2023 10:58:22 +0200 Subject: [PATCH 408/412] accel/tcg/cpu-exec: Use generic 'helper-proto-common.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only need lookup_tb_ptr() prototype. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230611085846.21415-3-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c0ab00385f..179847b294 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -38,7 +38,7 @@ #include "sysemu/cpu-timers.h" #include "exec/replay-core.h" #include "sysemu/tcg.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" From 708906dcd6e10c3016d2ca1e76e3d40dc30f2fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 6 Jun 2023 01:02:16 +0200 Subject: [PATCH 409/412] exec/cpu-defs: Check for SOFTMMU instead of !USER_ONLY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to check the softmmu tlb availability, not if we are targetting system emulation. Besides, this code could be used by user emulation in the future. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230605230216.17202-1-philmd@linaro.org> Signed-off-by: Richard Henderson --- include/exec/cpu-defs.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index 0d418a0384..4cb77c8dec 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -60,7 +60,7 @@ */ #define NB_MMU_MODES 16 -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) +#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG) #include "exec/tlb-common.h" /* use a fully associative victim tlb of 8 entries */ @@ -89,9 +89,9 @@ # endif # endif -#endif /* !CONFIG_USER_ONLY && CONFIG_TCG */ +#endif /* CONFIG_SOFTMMU && CONFIG_TCG */ -#if !defined(CONFIG_USER_ONLY) +#if defined(CONFIG_SOFTMMU) /* * The full TLB entry, which is not accessed by generated TCG code, * so the layout is not as critical as that of CPUTLBEntry. This is @@ -133,9 +133,9 @@ typedef struct CPUTLBEntryFull { TARGET_PAGE_ENTRY_EXTRA #endif } CPUTLBEntryFull; -#endif /* !CONFIG_USER_ONLY */ +#endif /* CONFIG_SOFTMMU */ -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) +#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG) /* * Data elements that are per MMU mode, minus the bits accessed by * the TCG fast path. @@ -201,7 +201,7 @@ typedef struct CPUTLB { typedef struct CPUTLB { } CPUTLB; -#endif /* !CONFIG_USER_ONLY && CONFIG_TCG */ +#endif /* CONFIG_SOFTMMU && CONFIG_TCG */ /* * This structure must be placed in ArchCPU immediately From d7ee93e24359703debf4137f4cc632563aa4e8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 22:55:16 +0100 Subject: [PATCH 410/412] cputlb: Restrict SavedIOTLB to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 2f3a57ee47 ("cputlb: ensure we save the IOTLB data in case of reset") added the SavedIOTLB structure -- which is system emulation specific -- in the generic CPUState structure. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221216215519.5522-3-philmd@linaro.org> Signed-off-by: Richard Henderson --- include/hw/core/cpu.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 4871ad85f0..ee8d6b40b3 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -226,7 +226,7 @@ struct CPUWatchpoint { QTAILQ_ENTRY(CPUWatchpoint) entry; }; -#ifdef CONFIG_PLUGIN +#if defined(CONFIG_PLUGIN) && !defined(CONFIG_USER_ONLY) /* * For plugins we sometime need to save the resolved iotlb data before * the memory regions get moved around by io_writex. @@ -410,9 +410,11 @@ struct CPUState { #ifdef CONFIG_PLUGIN GArray *plugin_mem_cbs; +#if !defined(CONFIG_USER_ONLY) /* saved iotlb data from io_writex */ SavedIOTLB saved_iotlb; -#endif +#endif /* !CONFIG_USER_ONLY */ +#endif /* CONFIG_PLUGIN */ /* TODO Move common fields from CPUArchState here. */ int cpu_index; From bb9c998ca9343d445c76b69fa15dea9db692f526 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 20 Jun 2023 21:39:47 +0200 Subject: [PATCH 411/412] target/hppa: New SeaBIOS-hppa version 7 Update SeaBIOS-hppa to version 7 which fixes a boot problem with Debian-12 install CD images. The problem with Debian-12 is, that the ramdisc got bigger than what the firmware could load in one call to the LSI scsi driver. Signed-off-by: Helge Deller --- pc-bios/hppa-firmware.img | Bin 719368 -> 719376 bytes roms/seabios-hppa | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img index b2cbb71ee0f91623d038db04d6900a5ed17472a6..e7660b0458b78b101c7ed2dc17595e94438ffeb8 100644 GIT binary patch delta 225597 zcmdSCeOy%4_CLP&Idg^q1{@wm4iCeF%9DtSf{K8OiHeGfiinDeii(DbMFkBeCYD}N z)T4%lsbyw`p^bIzbE&LrWnD@uEi3AJZ&O)!D(iB~^7mfnoO$SeKd;~Szu)WiBlpZ+ zYpBbK+;^5xv8^<$noIgn?S^=U5@nbn#f$TP?B8b?K!V9ko*nQ;Qk%w>%U zwYfa&UZK&tA)RL~^>4I3jJ77js>V%^{@Yv;e9W4eOSFxx#lpZrCU7$o6UN1b1@hr0 zV^RPMdt3Y-)@Ut+ zhH<9WzZv>+-uk5>_W!4!e>J5p7aARteY&YvS)b-@obpQE6-Ry-oAPnq%T_CSp-;QR z3V)}Nzr{1w6v72sG+1iaza7?rgN+&5w@gbms@$Ak^FWl!N5o!!|(4e>sgvf7xSYWODd3~5WSHVXa+ z{UWnQ@``W1k_@ehV-5)7h0d-qiwsi6lv`3pYvMQ?FYG)r?)Ok3@=WWIx$g=6EUk5= zF+xOl>%P*-;g-u-h`Qf+_5Nj zc2=1oBOx=86ATZ`%Nl7YZcVIME(ng+Z587Lp}VuYB9{w(b*+{qqlD=E)}keOyuEYV zl4*j#yF0H|26I6;)9PF{LI`W_Oj^D};Ky3eRDB@G^{p){M+t%Xtru2~=N+AvRR?*W zVHm?m@fAmGzu&S(_UUOoyZYa}sABBpW!V#U*+R=P3FK$iSI!$2Rt@T?c@|MnXTbByFt}|)d!<;v_p4*-P z^^Wc6N?oUQ$6q*aY3*ql2D;FleK@2i?s}680nScm>lcC`oo~Ig_b#}!NXZjHv+ckS zg5a~Sv+-ag7Ys)_+aB@bLZ8CUV~^g$h1izX#6u&5SZizfp+|`7=Ak>`|I3}Vhli@) z$BqmZq-M3l&ilh!zkhr$Olf}NIYEd_?94fSuK@eboY;i%$a(r3Be!%WoqB)^lB2WZ zbgRMFViJXNl{sU0c;}KA+qfWJe(r3FBp5DtroG(B`LWKPSDv5=(EQqbjsd&&`eZcL zbY@)m$VV_Xb($~zD)KX(rpto`ZtBeb=yx$)s#d2_m{0~c&O({@%{Ac^;dWqhSGKVN z6Sai^|IeWA*2+)kW0G`z`XRiw@3TK6WJ`KJMo@HSeDPNwDTBtdHRGzdVC3}IG( z;agxg$Qd64KjR(B^;n+8tCU15 ze?$l_QqEcVa_}o@aeOsw*cZolMH+guwlZORe13Lj6k=9_Q+E8JDG36T4e7^;p2VqZ5YaL~HP^VlQ z&Zpxma|B-^1V<_RM(`ax+n$ug2Ly!6ZKeRB)MOFnnxcex)Lq9|xmKwx0BuB}a=w7) z3WjFIG=awphI}P$0w0R#vR6#tDO~W~XWw=kZ{oi3O9ubC`dsc;2}-|0o+w>SYBXI` z#uf5p{-RP{$YaOe;g~DFYcdLDZ$w#>;xIzn+N}|(6^s?;u#Ah1i~izZ{6)lZnPa|r zMq1c6U&x`EeKBru-o-Pr z(-dLD#`Ae#5(UtTPjZi%`U>+LV=dL@8p{C?&02ji_atV+?-MWP9(}OB(X#rJ+!p}% zthkzc)KP8-%K9jG=fun4ixS3fYmZ}7uIBEac!1?x>?b?F`8=SG^T*z;be3NIjr+uB$T zS^doy{dT*Jr75l9s>x7CY|PVSEcNhWgzO|k9Ag77shx_A`S#*jzk^N3a_r2 z6^#9qZBux(Kk^R8eCb^e&q$6Vt}%B3lMQBczD=U5|9O{-tc z+v=EaSgaHj@dv|A`Q{w!=E4VA(zcqoGlg53)iF2tC*@iZj}yyce^N|Sc{D1gm87XW zT`=V3o#X_o#AWI-h2Ynu-;uXKyht6QbWx%8U6k1A;aqe9PWy>l&qr zG^K&0n-(iaS>MyhXL}tBLWd|BGkA*7Z>my00}hFNL%A@6&k=%8D~U6CZZvCm%nu%9 zZm}FdX@!|>S#5d0CNU&XaZItzCH#SS--9&J39|w@ z4YH%G&p)!td@nm@N56*c-Qk!WG16|Gjd>l|XN}3>XEHAfZgkA?IjWS-!SWS|0h+__ zi;w_Cp?^@%shSW~FN${9qbH-?HLU z#zTECKp%1t)}bVp@l`_1-AYRt&+0$UYt7&1C^H~IB3H6^slRb|`#~#16>^cF6Wzk1G3759>;>%IK{Alzd1?z ziOg}#3!F;dG^a8n7*kp1_C1tUCid=lrZF2!a4F&7p+L&>9A(kpt9<2=8|Zt>@kXWZ zLY`>D`NzRm>#eI2hCwxbhoXA@B3|p8t}P_2N^vg2dXS-*E3mya+8q^q6&J#Sl#(SpYe_|)(>5HbYe(b2^M%_HiF1MvDQB1PiMDofE#^DB*w(L>hG6y8 zJashHfSnsL24=PtUSaIRM$Do!YZ&Bla;C}ekpvd)W*1}?v_o_B!wq@yfqjMB6Qev|*9bgbchd6RN}4YC_~jdFbr9}pP*3CJ6f zKz!^dlN%K4S{}>Ol>D`b6^JPuYN7}T}?@3 zfYP=WYe>{oy-M3 z_+5OXa^oSSd5e{%ZGhIO3_XVu8~UDPI$#~P`SF3C-9)X zyWrKx@0GM&aA)5QO7SipV>9nxdUedftSe)7I_66Mq5bcT`u2U0JP+_ZGe2Si+F#1_&m)?7j@kZiWJWT} z=8Dlr5hCS|Sb2ME z)63>|Y^rC28#9kGQ}Yvf?_*Odb<8$>-hDvsMIB3<>5c9KkyI~7{bcunepFxBeITMY za|JfQ*`}?TRjesRYKxH~>(6 z9d&8S`?>o~L4arZEKm=5!D@X1ja_CWH}34pVDPmO^g-x;BTSkHywx$=@O@?=#w9ie zd54cWhUz$qvwRLJ<$Dnn!IPB6y;wm4-&flA@*jlo24%Ox<74_j zR`ai6gK{2p0za)dh%an|V&0GAn4}c!=jrJ&dvMO0=7^2RQj=6eJInf%Gm3in?WctC zR@$9QbQtFD=aB<^)P%xOD&I}ZSL$-?y<8}#B|kHcf4zDm?!@Lt9#wkw^KpTGFK0G! zv&uhP~zcyS*esB-~;@^HmYu#Z5*jIAK(Kyw|5-izY9Y2 zPNm}@p9R?P2p@%0@>YBPqcn}fzOpwR;*)U}ovm~~#=jPV?pDqo=Cg)WsoTr!@MT!S zj_LKOsCT7SSQ*_E6^=4hWR)?Y3f~Tyk5TfE@V3izRSVm!Y!y$_Kg$Dgp%v_8{>qZO@g`Getn97^R#*IRN+Aaw@np7grIFszPr@P z_w+dVpO}*?5%!Mh!p~x`qr=Jmc^>65iUXzBerA@ig!_GU%*nn!k# z{xF0bbU68=5h$5XeyG;TA1g=MXRnzpd?sMYFD@1SIu?5R1if2`$04@Ta$~2RcmlW#XkVUYH>jJ*l$>)}6bW>3$yo>LD`?mdW$p0^J zb744&jnBRGzgt*Z;N+#{C~zeiREqN7hLw7Gh1%v>G#cvl^iY%zlnfNenck?Ec6=X0 zp|%d2R+&}=7n@NsQM#R+TsI3$v$mm}@WNe%k~Fxr=>NUUYXTj3U3BwG0hcDwk9Bt?(dcR{ zjB!qQC!2Xn=MD14Q5*eua5MHXW(kGt+mN#`YICwu9cLp#Z=`BW2oY(U2koNUr;*HHP~r%}s*bznb|AR;N&l<#JF9 zGaE8c$D)*x#sWO*!8rED!&%9l8X@#_#ml6cW)I#QQ4qHwzXUr2CrH5mEK{ z!e_o2UT`MheI^6?O?_v+ZH)ODJg^^del=cjE#RwOa1Uet#HjQ88vtK58C4i(R+~Qv z%qE=xI?M(V`ha{dxBxKjwp>2}^?>(z!RSChw;Q&x zfJ=-8=9sukVPHPsDmMWOtOnff1s?(2>jmFrtWOkR9o5GQxX{zyrwDMH7u=2k(dGbb zg#RsQvy^)XEERyg7%aqa)vLXSv7joChk{^Wkf%p12ptMO<7p2*3%JL_AB^Y=3Dsd5 z{}7BrNQ8$VBnoh*7n}{a2C$9_!5D|&*_azX##m^($Ah8pP*{`Rj<2w0lnzw%pRiMy z4ZBThSlAc>t}iB-e8z+v2ox|zhQ(1C9)_q)z+`~_69ogJjsYeVO4JF)qAi~G=up7* z9uGt}G8R*$(}N|Zm@%ut6aZfGKLE5@qdWqvR>0X_a1P*WUa*6)xFo=Y0)Jc@ikHXY z;IX(a4}IJ@#^Sem;P@8Y)-)4L{>M+kHpcqrn@lc&{R;pWdKmf_0mgF_*H8Z%z_ng* z9b*IQ^l1fy2G%o{ly4GT@{%AgsaYQ)v?oDdasgl)$$-m~3sLGk0+S&ix!nuKm?nGm zC>cFUaYr%gAWd<2=u@ua0;9=eP--*aZcmJ)USce*#AL9k7J@IW6y>^yAkE3xpnNot zOq4qpT=3UNC0O zXsd^RGz=VV>+-DCqt7vxlcrn4P=`S|PR|(T++b`BEgVFGG6o(RljETu0}qW+qZ;k_ z8MBSCT-*t|=yMH#t33wg)*$|Kk9h>-qQ`lpK>x{uf;^`#04~jg0r^dy7|Cx2>=jh` z2&%DOE9KZ8#>OQ<9*IO5mxfZ}1(#y}jXUA7h&J%?*&diS@A35>h2t9mpYwvxGgi={ zdk_W|oMLQ3sE2<7Ixyjqr+va@#wPZ7hG60~%>P0%4gct;5H2dL(gi?ZVKvGnT_M2` zFe%gH(n;BX&wJ=6U0`f7&1;f}GPw|?$3s8)8e>x&Iz5b=V!O`R)DoQl!lsroHjP4+ z1VG_5cwpKwkHBds7@O{z*Px%?jB>%#m;i@`BF*_8KBvaSvlNOr%Zw znPCQO1+1fHBmyq-2$)d}c(+F(C2TYHdGWUazV6lTWNc=>C&*_O0Pb*m#HMD3GaViU zGvSe0UM`-6Ue5CB(X1-KyFCWZf&nE4k3x#Ck_wN!l1jjh9{Q3dz;1mQ{}L#iea<6b z_Ibui6HU1Ffk!X|rAa7W29#C--tAF9>1pXH5EDIqO3yG>R-?CrwyYMV+0$OOjj@HU z-PRTX01L;$8#)2L7WOi>h_+w+qp~OyrC5i7T2zAKHER}i0KV?k4wtXaF$KChusR=b zsi%E)Ip8`CR`;++)IMqg#{cnI>zJ7+af!AOW5{vNnyenrf5T1=rL7_Ud5` zdblpiBX6A*aFxen>#6~3^s0Wg4h(mA8E^*_tv7fKSWoadkAU^(8LRaQs#*k9Z3T$% zkA7+~=Cvn0^tBy;9eO*yYOgbPXEAMFq#r%H6E3~0&C{d1+8L`W@+`e|#f;ru?pa9g zt^nMt_YnMd-(>9GL=XMFNq~=eqW<0!jNMo6@!);nx3z#?-TxuLwwtkyW{-f4z};{1 z!1tR0yIhPO+z*d!qKpausBE&LP=-V>el|H6YfAHYpeY0JF;9>;!Jy5Flz$Ne%H|}L zdQ=F;&*nyy%UZcgnMU2FcpIru&d>zKut^&qdGdu>g zW&%FriK*7JjP1^}dB$irlv9eK5ikbO)mtd!*X~KXQq&cGp=C z%IVzwSdvGXE%dAdbew(ME1!5A9=9Fu)_WMi*l`Ga3PGy66at?zqj+tXPhDf| zM4iWDC+Y#aRz9$uIK$Y}uC*WhPdgcVrqMG?o`J`nEzpMq?avk>{+>PWaoMvM80+ww z*Bu3now}hLXhpr3vD2459yooKvFBX^Gf*!?>ChDre2OuOS?4)vy=j9B-@M@ox;N3ow<5r)|GX6ixWp61Z$aSOOg%cO=Z3cpjCFTt z267AP=;6D15=-#ACm4G#%@bqqWia+ZhpCThUXyzT&d$!T|yxOOYn=Zz_JF5%W zhdSVYop%$ke<3D*>GCM}pj7OvAem#tv z%`W*i02Y)|4}rOy@u2g14^ccS z_>^mm&+Bq;HhkVW#>*S^m_cXD(Sfx`v>324E92X%8Qy(Q9!oV=h zC04FWvf?}K*6)sXrER~EfG5da`-Q$?^^9+6^muv;Jh#0{xxQb>uv-rZ4~Sx$gX1nw zR?Zz11{hA?yqOIs#eA!~SQYDZU9$cWQ}T$g)VJ9wJk^VG85|LoTfMlF6`nGo)S;9T za_^&p50M=Q*>M+H7s!r->^R7dt7J;CPWL3}p1iD7JSxQcg5pU~JgHH%>lDX8aZH!g zT?0wSKz9su$22;pPWJ@pp18cXT@&;K2%gXgaDA+r^f(9}cL~}EL61Yw0b(CI{W^UQn=(Xg^Ne z6*!SmIaz-^%NpB*?;_AQI$3_R6Ng^FkV6R)bF#L4CF5yfI^NMd`8;p5II-~2oW!X-@ncNV2pTw5YMy+WQc3~@-+;>HhF;c^w`qIpR=q>1I} zQUvfZWNNz94~B$=;tLq76k!F$us=W<(ipU*TBU>>w8g15gib|ugp`4XnWgB{aJ5g8 zacHhGw@Vz~Z$|?Ul~U1~EB;F$*Y0dcw*u%@I=jT8hzF<{P9+SQQ^n?nbgJ9?zAVm^ zgdgja)vt>QLbH#(<#q90-sek@(qoH??WIy*N7oLZr}8d zn85ja_9y-(p5oTi8@M3_Y;UR3`i2^p8}rEW97yv8xpMEjVqU-3*O9Og{Dg%7%_WmH zhm>_H7vB}j`Ee!VJ+WWJC4UT_F?ym4Xk%^}^40DKC5=-o}60`=PkP`scfND12+QrW-k?hvp9m$`J1X zxA=;(t?VPHMmh7nxQU-pa()myI8(m;LA)vzi%%H1 z68EDRs(kUI*y(#rZ1>@D_)#i;#G;q+qc~9c=M8ZZudrwQBqnp=cBg&*&*Etg1A=1e=<>gOc>SRckDhl;vv@0V;iE5@HZhdiYd?=j(VXK=gP)CJz)-I;Qb3Y}H{ zoo0x~dKEdyP?hxDd5C%vqp=?JgA}58t_!I2Yes&j2X#g{KFIKp?Kos#oS#3`A01n+ zN_!HRSO1zn%!4@wOp=mMFRk!4m8Kn-_}Kh>4^0~|w>9UF^4uolW?1aMEni6Tv}{&-(+x{)O=#(RXFMMx!Q|&va~dJ# z_1Sa636rN{>VetXFek}_sRL%fgL9HFJUUG+Fb7|m(`j&b{SIJOPoC2mTu_r!O zWEgAa`;=>?hTU=d5JZIwW3c8SM4oPeUHz=Ffq1PaeXWYwX5T;0kRh&UMo2D5Mzbsi zt*73Ri^Wxmz=XaK^dD-iQ5l+0jo$)vUO><<9zrWT)0P;--}0cMfa*U#h+lA_gr^&o zj0J|+(0a7obvlT@=V_@^mM$b)0I`g#Fqr`O<=5^21{I7sqlKkC!;rAg)VbWu=p|&t1enu0bSyw!J=n?o}LHP z>;DrhJ>VX*r%RQ74TiivB^VCNw*zSX#c&kcS2P$bMxJl4+hpkV2`R@=C;feO4uz&> zNvVC)LxxOK7~WuH%-AO<77_I)(L&rO+U*xw4R?tc=>o+tC}uZAhx{1!x+?la%PU|A zeKm|Hs`i)IcPNHG8O9caXvmy!bv+VS780~1JW*rW2JJiE4jJPez_fkTkUo&HIWMS~I$-9uHw^P| zl>$><(2(WfG66GkUc+QJ*KyKScYDKhH`j3+DXX_M%yP3GCuN6DH!SzCk+Q&;hSeT6 zQkMK}!|fg}QkFHP;eI#Qlca1zcEc7o*OR2{o~nk2++0sK+kiUtuZBlFY@{stXv1L- z8!1cpd&3hRHc}QNHN5ENI!4N#|5L+X++4><*;(H3s+;Q=bu2fd;R6p>U78Jh&5?$W zJ#3`xz6TpV^RSV!*2f!uaI-x@%69Z^__v$u2~svCrr}pN*AvZ%oZg;rl1D?yoZrgPMF)k|LkETm4zo7_+LDXq;kv32L7I#@o;bQLdIHJ8~BHA&cmc~ zPeB9!*v)yERCavWz`t{G;`|SkJfeaB=wYk^D#qTx|LtLnu$R4M$iuITD+ewZCiQ1n z4GV_KgRt7L%j9@RH!$X{ayqs|E!{XAs+it3OzcC;Y}f zhUk#VV9Neb9t}OAU#sntlx}JmgNBMqIS&`O7bZ}{E7^PBK@O6wjC#iqW2=>1T3)2o zL6{=nIZI}!V0=*zx;%{P@>L!7~6hA zelmt$fl5G3rwIVwCDUOu!+h9aAFtdA^0nocI z;?kr)pjEi%WJmpFZj1(WyBSbOrObUGgymTP9p4~x+?TWN+EC@ozacyERr+)rqL%xU ztexb{gHE^zN?7+f7{>;i8G>m~kIbuysTRv(vrK7|LS?;+I2UZ9kFWuI#v z)Onzq?~r}I2IKUhDkDYvm~7d{=|N=!b+}vh`Psv`5B?ofoDyud!DKb(e%=h!;(w=v zB1P$2rHZ0lH-A`))yi1-BEp=$3CvwPQW6lz;eRGfn-iEPo=-`N$0G>Z-i0;?Fgu5( zq(M%^rz)lgnAhJ=83OhFjH(>sn!Ye4Glnf?F+C~~t<<*XQ?kJpbC-%a56o?cQpSKS zmU128G>LxnLP|biid32|V7fz63JBAw(p&BXlXfX!==gvLB6*$~lSYyi8)~ z^AE;|oydSsYGMXNN}GuExf4W6pMM}VqySFRNZ*Onyz$&Ql@yN8wpLk?YLdqy#BlOx*5emvKGF)@fu5W3`^1%2Xxd^A9~Wz5r6Y zM*24wDQ+{NyzswCe*tN)Mw)NcrIxFt<^M(MGXjf0ezBA$#7V2(rAC#s@xMtCqdt0! z4v5o9)fhcRG3q@kK2L-6DNX9baeD8&n1J5D;njN|_|r%Crzu{ScvbB^KK~%mggn61 zUz6AnFHNLTqUXfg=O0PufV4y-{VHC1*;Wc!`*<7U{RhiTeD08wUV|iC%A)cm#uJx< z_PMiQx*cT=0negxFUsA_#Zb$zO^Pd{#u3AA7sErK+3I3Y0PG=#MU$Zc52(PfA*xVP zk`tvs+gz-E2H;&qgB*n&m$R(x(bpbtmvfz>aZ@4`2IS4;X9 zS6Zm5Yg*LE0LCH623e%+Cdx0QY?`jj*Nhk7{ZwTqzXe~Ssw`1ehLa^9FdXtv-U1O` z_V*1VF5*Cd`1V)*QJ}ea0FJz?ggf!3JcJYzj^J9znSmD;dXz8vOT&1%g0u59 zK7kf2wdO8HmfXUxDOt&xvA^RF@xa=FZ^M%L$a>75pnOc0{2mQVq_ETEhu??FVB&$l zQRjEkkVT_qtvn&*$hVGV8x;3RK42>^T9hY(sw z?D)jph*9t*?i}*RKR}GtKEMoyvLA?ndUP73yGawss;EcLg9J-=Kr8{T+EAN`Qbf`h ze(&T#6(k*>cxBBfj6)xj-h=c-=ybp;6q2zB9S+!lvYr6sL;;N`_Ypvncc5%1Uv8z_IpQHF`-7#vFy z!=7eMjY>o(Q{uo@jDG2kKt3ohXK*@51<#`rJ|pcY(On+ zqmLH)Xd=d3=v#OUV!lNAI{~Lq`3>b)0=jU&fN=`+Bj5rm@L3?)xDc5_ARcT7(tKQa z4VC#Q=n8qpfcUkJ zF2KwTMEV_AHyL{FQfbH&i;Os90D~b5d=RCXfEH9R69Z|9TG)gN=1O2YX;?&7+R)EH zO!BtN^;`;{ttLrVsQ-?FZBPk${!tTLc28lln;uT0|I>(E1YCjq(m8 z()vOZCGbZl-cBReC6?>iM`7Q8RFFx`n*en0~%C}6H2fg&h4<921y&ZZJfnPFN(0>MC!*FYurjEn+k$K zH5l_6{kL@WW9;1{KvJiG4C;dt0OZ9nvS|WFfR0c>kth*BTE7G`T1O(Hf)e7^*WLk{ zxB1fzG!R2k(n0w^jch7ZZl+6eV&)ttUzQ2+X^ank)d(M#%D%~nlQG4ZAj@)?_}H_` z=)sa~%OoV0)Sxvet1*MumQW+udiBoVfj7G_lxu5*yHqdNLTnV{)svlkftAu4eBxv* zn`U7J&qzJW(KeSTsvdUoMFt83d}8U9P1}e9`3|qb#0jcHnQ$jTh_#>xQ0_;2RTnCc zptP|%#&hw@h>I?$GBBf&(gwl;cU(v(F-|s+Y&G*^vvkgs*AC`D8i|5L1L#3b`gvs)k!1X0L zBdD?c<@;zcUyj$2RP^r}x(8^iSj!6tjrJ8?v$FNHx6Ed1%$aNHCr$WXaoQW}B;U@CU4G9=5KjCR8Nm&4%#e*3iFxsgFu@XKG zC6|o>^oo~~qP-PB=M^}Wq2wwnUCnh*wdKu_MI0DNIaGJ78;$-Q$x~>z%wUm^7 zfP(E+Q}!KzuU#A#m1Bmk>|t+ZeKAe2Q$blJrj4cyX%8vO!!ONNR+CfHD`j7cGB#79 zr*ijZO7!l=flMjN?~K~ILG~S{bY)7m;&pJ8Iz`e?%03%|86WX)cz!VUy@(2wa(F)d zB%C<71<*#6b@2S)4q&@Mn;8w@G`3}o^QsCa?(0-`441-eHMBaA7w%OT1Ed)UJ7Mra zu>Bou(@7wPGU9oZQ)<&!X!;kLR#6jN5%C7ft7_9tH2sRE%`}$iTLd*CK%wYd<@O?vYe)I1eU2(Nxg zH@fSgcQLyD_cfLV{PZ}W<38FtXku$p!qzbZqq!o$UdVHMp-oo{8g9Lr7;}h zzIO8J3|dJB50rhD`hnmZ3Tt$zAG+7?H2_0=Q9-8A@9*&R;DIn=2qL53uPFb9%Z4NX zI|7W@4uv7MBy6JC2O|9`$cK!8cr~`DZ8XrxaU%y(e_#bBGA+jrwL0LpQtw6V0=kGG^B!x6`#Ip|qN!#PPCRM<7U8rlZUDvLnZ!)oFCyaZ8+i-N- zi8Kk?)+FiJPa(sNtkjV|dLy@KVn_P9v~{Th_A%j$L5z>Y-=6gdxTFafndw5p{LwAMW&DLpRDsf#6;y$7r8}sCNp;^DlA(pFjgNj* z7qY;lYDTQmtQ-Ajm+b4*l`LvoYSP=@)7vn!2pyrLT}Poc*(y3;Lt`3hb7ZaRzNJ`X zNkL9Dp_SdEC5u!Za&p%s@L-7e?sgLAZDS%%)xZk@9oB!>KniLs<@ z**P71gv16lHol^6?aSZ+m00agP~-hN+NrDgOjF|;gM&d;`XJEi8Ks)FI!D@7ISOmm zkb9hLofWgEh%(;XzO$wuM?6ggYZWOMwrOg0ir zP~>__b8`DaWIjYj$);Ro@fax)k601cWYuB5$`#RU8DrvCvT3kt1@RSFwB&ej6p8e| zb8$7Yv>{l4kyHN(JIuMSWm5_g50+QP*!+LVCf@)jz1hwXnJ_+YC+u8V0rV&WV9qNj zF9JDl8!}wKIAHez^EJvfU{5_Do9OumrSY;Kihw-4G+s6ZVDV>pKp;7n)8f=;Fs4Aw z4kk|ratbK%xW&{*S&%EqOK6|oB9nc7 z%b+d+xNRQ*9Pr`Fnx#0%_}q$M_?`YR*wc$sMfyzHL`hb@jO3N)`~MLY^hc0kkSt<< zyreQ!_SvA00qHyjH5YATfJ)OLoOpaTOwa)wYJBbtL?nIff5);~SWu^W7NTEOWS_gS z=jA^Qpbv7KdoU33Zvp6-C;L2$^qLI@uyc;=)8PVMyHoZ#sRCrpkbCgK29LzEl<=Qr zpJC*Ku_AaL1KDtwp8MaGeMVr1A1nGVh5jrxSV8pHW&o;X$Ubj>LG~Gi?>m2xea0|o z2>qJ&On8e8?#m|Oy3k)X{s!v!Ex3fafOtDY@iwk1rM3>eN-_S10|1@oWXu5>TOhM6 z1p~g-C~60CcO29kZz-SWOCdHNrkaNd5{D9o3~}*yP9BUKX~wK;WFJ4A4aRv;h0}!oAIjvUV;Zo@W^&dBrHMn+Zs&hBCi%)O{rPAhMIB+ zKws`asn%wpaR*XtI$=j)dPfoS>EC4I4y+d{@-UEx;+EEI0DDUSoP$Xmg`_y@L>#WJ zjV%;BdAM|ox&qv*aW%m0MA!3=F&2#lFM1#Z&Zv@&yU=%19gQ)KwgEZo8QIuM$j^be zfkMw5*BzCOyOo#5;UHdAfRoTN#>dyjEyeNPxK|BO^tKSCfLvUL9dczAD4NL|cdITg z)0cwrl#~B)DJW8x0^=d_dRe)?6qF&4qFrV@U0xEsw!4)z;_g_*V^>r0RoM7f>g!F} zIjyk(f0}xgfQUfLJ$0%{*YuW1YQbTE%1L;j#*ezuODLZsS-O|2?qekodPFsfh? zHEA82U!{}Z`9~>TI+`{)vVSEm`<#4!kB(M%IYJk!Xs3>LZQ3g<30;n*k2+i6+OJpE zFdMv7-bk4-tqi40>EkjYY}qo|DA6*J=cL~cVgWrAVa5NGviOb_7$qs479fmJHA-p9 z;tawK)xsv`4d7l*SvZ77ueHEsHa~>chS2BfRQaUiECR z`be+(5RXNc#X~%%0Pxs=NQ_()ARB%34J!5lSX2n?YwyZNld@)lB=>EMLYDP7{>&35 zu=>fENjCZ`kD_tdS$b?#giqY^7&l_YxvdmS$=owoTi!!?hmNqt7xCwvb~8-iG+)_x zpYrVlX?Q=x53Y-;d2Sh+)eGOWDA^dMjJ{18*FVv9&5m?eO+QuA8MxiTFN>3;RM~jD z^4M)saw;xHN-x8JQ0cYcVMuQDI}w#(P7nSfRJO`6QZ_~?Ki?*;nOp&4oGvH+8|Ka} z!s@#UrHW?Dv}&4YSbL^9w9Qh#TGVb7Ve6dx(AKNUE0&G2^7KS$coKqYU#DvyuA#IH zO9%CAjuC(B3dugqT_YP~6u&}gczhB`hOX(C-5!{IKm8dpRqAHh7^f^QlxB^t*Q}gW z2rFm*9j}l*fpVA*U&T0;twdU;J%AxaGmy*7v#0`g%Er5te?d_}yB6z{Tt~0DH>r!o zC-enj9;PLw=aa6IB;CzwuGdIP=2fa!JXMpVs6coej2KgvBO4QyU6Z6S_-iQoa}bpi zdx6eM=9K&mip#x`f zUZQ+4S;{DcnP{#BsX;b0^qYbJcpZoq72TqtH{u};{ze+!-9=i_KU5bxA7t0np?qI9 z4pPddAciYJ=hU>u;V+g|tIhwAjTy?ZDbi@aF0x_}revY=Ex|{qeNc#O%u|LINyD)h z&;Zbd>8u-Ize!iB_(VD_8;2=PMN(3r`b@e68v!eq@fQ+bERqJ|B1R40sT<&+S^vbd zJ!I4a=0S|`WHbI8-8SkAK53R3uEWPupT(WTlY2n49HkO&E5TLhlka2LML&K+-JDD6 zDWtGMmNaWoGu5?CR9+|7m$WhVSs;z=%)og7)hRG2PBuDl+O1zV%&E~?u-*K3_e&Z8=twT0WQ`SwBGNPK%t0*Fwfm<{TDN$fxS&eL5q`Wjun%N&}G?8?3mDo<9(67I~6b0(aUp4*i?(=`{H|C=oUiHJx_`iW3dzybwXV;c(H3vFKegK zp(U~EZ6&{0ine`YhGlukXJ-Uh)SXKDCP`0~v8GGkstY}m*o3!b={w905^W;t&EzklMR2-HXEqT z{--QG<0kIViJyt{BJOY#cb}7`11{p37M=LO059UU5bVH1?m_nyA6de0|MQs^|4O84 z(j&-5XNLJ#F1r)Jqp*HvB&bRU$WptqeTEced%!Juo-X-;WG~6h-o4-I_FL6iS(@VJ zd_(7);>|hLn{%4V3C%bFO4IRhr~7t)^@wPbid}R#MM^W&BtJ;E_a5prt!ws*1GW_h4yjGD7wtwS!-K^u3 zg3Y-D{A+w=!)I>(8#@1I3DnQWu&h2*CmTMeb0)fH_(GKw9d6D&I_Hl5 zp7=5B^yb{<=G^|1Y`Bv+Z8T*|{-(3u=`{j|I&apy-K+3+%f z<{YVVYSCsGH$Nxn%?l|kZ)A7w+L+CpOR|Ku>>u0va(W`D1X@kei!k4vPe5Z$^yDuX|+9Sjy_ z#%w9Xw@M$}P-V?*DPij8E*W!HYBI#nvAwt!7?JMX)l~qdh}Tq8FgK=rDvSS!L{_Ff z&`pAMXKu>kKb22rOR4#%+#=u7MV?Ycg0b;KS$w`P7(Z6mxf;#8BE5;=)^bKEoP+m( zHoBzDEz_lJ^cqTWlQ&C~*MNwd-CY(RPzL}??r4_9Ey3^>-DbSG4FD2zHdu3oxL@T^ z<9zoLSzJt-)hL>KNLRhsYiSghdaI_#UHQi#wYg2E-z4I557EqH&tQz&UXjIN82Hd0 z4E5aCEo(R6KrIeSRcF;mT%@g%#U#2gnfrxBy=4=VtYCefZrN;X+9+8}Ks*h(rwk|E zFJ&=20Ix)coF^!(js-lqn0pkvVkoYR7j0oxJ8P(N12% zZ%Fd#Ek6bPSmNWvmq3VfC$EhbScO54# zcO54#cO54#cO54#cO54#cb!gL0PxfX?@g;mE>9DVTsoi~u7q3q@Fh)wL6d-_SY^sH zrMMB;jT~*tS={Wrjzoa2Jza;3-T2#Ecy(fuEPUqj4<*yhK~T06}Zjn$y9LT4gq&ibCZg*;rNpD0?KL4Pa2`-Cs&>PLHP+t ztY*SBbu!^sa&UQlhC8q@%7NbtmYYG^L0RB}!cxZmO^7gMvRlf-?&ZN_m1lC|5>kgOZ#Gsa9=J zu8h(KB^eQuoTUwl8#fcUY1*K;aVvparj3b))5gSAx6z=u02&lmod(5Kr$KSmX;55s zbx_;@4T>9}LD2x+f91ne|DB`m8$4lZbH^zz}CqKFBWpImj-Pi}zx^|noFM6=W`0y@DxFF$TW5?7sE;;NHNTy@nY01(cubIF3|veK0m9^=agwMXfh@p z5;&nl>0Tg(hM&}Da7sPe>hu}>obt;8ycJm2i^C_~Tu;W+S^QxJs!Z3A62sl$aA@z7|| z9?2|HueEED9}=>d?i(;R={oIW^oWud(n2t~2C8q@RtR-i=gg9Y8Tzo|FBmc55)JF( zllrjE7^NKpbIt{C9+V~8o#?OdZbJ&ViKsok%jpf%!*I-07(59$SX9Q6LC*O_YxI9(6HU9 z4)Me~4Yydm^44)J%%(nzTddx8>qs2BX;)Z`|7oVz6ZN|-B)QO_9px9Rci!t&$%Qb2 zv@KRIyCDQ8_renWDN7-uXTT=H(9L5#L`=eM0KFrz_)jF?hL?ZQI_VU(BHAW(llK4C z>{@jfzpSphp|t8Qrr*h#d`uP!i~zpVazmj&O-teR+2v>%M(G|U=!1Wig%JUOzD9z# zq@R|A3faEM>{uS1351b&*ojA6xP)5TBMa$xca#3QCsQAyE~!o-lJrQGgr#1Gqp#tu z`cho4FR4bMR}=UcmJ3u1p#b_Iy(vsA9Dv^~#Vcarvd|w1BmDtUmQT}V$t$YJ{x+QI zpq8Ztq4-9E1e@}TL_B~lg$q+L!IzxzmR6013SuX*=bx7a%0shhajhq@OQ)$~!?7Ey zQZpfy-lX(>RTd&F47VphAe7UJLNIop#8unY-+`W~hvnr!FF~n5&qiXsfeyZOFb&0B zG|p^fA`?%&2%P$Ss}^rv1BZp<)PEQ~4{S-61^O*KdU&1!53x2fsB({LkO7fQ4+GN> zGHI*f1w0HC47tec=}4Ql7o|lv6Yno;W_AL3GH%)0N*L(6$4~noAkTslX9$5_wNu%V zLZ$t+fH4utNag0ZYPC=5DW3ZD6UvXcMSQ9fDLQ6``boEl0)7e5#w#mujrbJ00~1-= z?+E}lswARLXb8*-#yh~z4^UyrjmJbZFy!&7IQ2@1|Av- zEfa5(E-QheMqrtkEc0Ijv7jsiI0zGL&}0HIYJ=vY%q9Rn9fUX;Q~~#k$+)atwjIzq zl+}=c$(CZ1`4uzjbf{joAL<4_2MpA8-wuHK@R19!k2~q(~q@LT}RH(gakbNLdk( z-lR(r5fH=zVgW%!^ifn43rZeEMMZtTv*%n;pa1X8=d(9wXJ=<;XJ=<;%h}_+^?9y} zw7Euo%R#Lp_i4tP3uNjxcr382LBukln4xjw&Bv-rikQ&V{Q=cVjW-{!0BANKpS7yL z*DzYe#hVMYsGO;~`GnRjUCtt1P7teY<|19IAojP-MS28;+?hY(&86t9bDDciB5EXy zhpvm15atp*5bY={{^J8LNZ~QUVJV-OwjOl>Jm~;LfUS4Ln^XJ(9DLlZ`V_j2TlHz0 zB{2ajF2$Q8F#%Oxft;RC4*sQ=BdU?r1>YR18-28sLtoZj?J+52>pVHJiT%?P;vvay zDfu-d7ud~UB45w>WNuat0rj(`mXx)b|B0fLLpr);^}~!wS#o|Q_;Wt&sZnW>A){Qn zJvD}qTAmzla`~CD4ujFcF^*WsvPed zZ_1YjbWI8dyXj@B3LWQ|hglBkhJK{mGf|6z;~fROmg_Sb9E&$=OH5y&vu3><{3xh^ zPH+~)Y=n9{XF&>@28|fRih-G{81cV*a+|eukXMM z#Us@4$}%Jew=!gtVKMaPd>(&1#QfSU;4EeQf%qarZr(Ija8E~q;kx2N;fh<`?FHfq!gA~mx$l}!)nwtq%9nK?8WQ?Smjv8Pdk<6^MhY82mF zBvE))r54&1%?Q=0&>m8^EtGeiitkL99@!s2Rh^SXXTuW>+!u)rDcuo}s=SW+zl$4Vh|hSIVZZ zv!K+K1*u0WRYS00{fdlE*8rx86h>4~8N=D?_ypt+sLhB9I@NM+d0Fw!007m%$(rHdD2C$Bx(4SM?;;ioK z%Mo^Hi(7zB5soJnjvWZqKB8W*&v9)|bI-vmNSQ`CJ5g6T0Hgs$0FDFjEN$mCrWF~L zGFAPNb`7(X8a2|6&VpoXyxqJwORxPNc5jC4DODQvGCWD~K{VH3{F@9^JEM|%VWeHD zMJ|sG8F!~~sU(+4VmN{yjI$z3^aixbkl*8`{GK&v)oJ^|?oQ|`pe^liG9Q7{=?|)u zQFaQq@U$OgN1KUi&?vhFANAZd%B~&!$V@8NNB9_%u5OKDb9E>L3OMHY+>d0(!`Hu* z8)v?cvW$M8*O}zq_Y8cL4Bjc=RbL)v5q$ExiDpzEWe6S>XI}aLVRR{37HdXxh0!a* zC=ygFM%&eW=!UZ_GG?9A;N}tRr!;uRGlZwx!rulIqd(i}`qv1IGmp!gUh?W~#sPx7 zjV`a=X1qi=t{Y%N%Q$m~*c2L|@iqQylQ(ufua!O5QiD7OS1iSuzCBE!#{3`@dXf7L z!Wk6KTS{Kv5+<$^uE^G_lktv-sqq~s2E>`ND>AmSKkpY{e(ukPgPHN8KUWhLj*1L^ zb*_E5abx?rxqR9w`P0poiFMADt!G_W6Uf6#**J4QgUQ$>ZkEh=kYa_&vb-z#++cIK z_92_Z80J(lQp4e_Z zSs3?&qod}KEJsH(GbpV}lJpOAxZ6K6u`rn}V|D*DM+A}=No5Wq$i#QZ={R$wA9FOo zky0PpkUM2yD|maHIU4;Mr|Ux-W={3-Gg)d!Bgz{zne$|0dfH{OB+CLwFg)YMnd81f z_ooMeu#B)&I>KLnkNil4Zl1Z8g|%rn7;WaWp(VOjVCM3clbI{?P-7m`$w{9|H|<|H z&Sb!2z(-Byb)p{QrGJcn-l=Y1esoY;nEp$she2kV{w1*^|gRqkj3nK)jiYoz-VL;4cb(*)juZl8hV3}$d zhpAGn4RL03w@TGJg3yvP%;U(_!Veq?aJVZGS?>`As6K-*jgBI3U(X#cD$lQ0`8IKe z`S3JaSYD5;{wQI-XgoYu#|&2I^9KHGb6LwW=FWApwJSxo=lt0=fVs|}jV7u7thDA_ z=EjZ~Dz1n#!(B1V&&3G}mByS@95GaTo1`i@L1|>eLR3HGih)B^&KOtyYoMR1sSmUK zRs%uR2%>fu?#7w1Zk21K155)L*d)%36Cj+|Xili($|2sBLk-5T8WRAPEgxql__NOe zI7`o5a#bCJQXZssI9_(T~g#Fo}TwQGFp8nznW6S+w)J>y;w zs&j>s3u9gZ<2LaNcfwucS9)WOU)=hE$c&p`so%XBH*sd%F7#ggoVZ)RtN`YGbRgYR z=64en!5l2$JlZo$WbiIwCcCSelTnY>h7t5&oM}1A6T)TCWYnf$(x>W~iKs+YDd>gJ zrb}+cnZ@1a%ZdXS;~KYlncBF`h%+9x-IS?Q)(qX?X< zmTu}ZvV6p*iw8e9K}aG*eSnBUUtAY=!Cz=1bk0bjQ|sV&;l5o+03wAhBrHglVdS!3 zd>a9+PtF?%?0r9PBBkBQ!;QErg4KxFD+J#Ws!-$D%pYwD5!DK0(u`)N)%ZI|ZBsx3#iS;_Qz4?g}40 zWX^h7z2R4s_Bw4mWtczLG7uS!bQ@={BeGU4w=S2`Bu;yhY@k(FSc{n4EJ-*QQ$B*RoC{BV;x{ zOSYVJmr>?^1i58@LHJyckf_w0>!MQX$tiZ4FPT#HveMM`G+s#OxYK2wK_Z)RDO+w- zyYO_}=?oWk7uavf<3}+roGIbJYVyCPH#z@dU}ya>w5?`4QO=k4#2t3^q)8e@>G{pn z-`xgjlJof4aa&}u zXIf2~{CLN>EpTo$!mvzBQOl;Nt^`_hLyqn)+x?MK1gqNv6m)a)Rg}9Uc8D-R?)0xdJQwOHoh33zy%nW+~ zle(ER>85X)x-i2|u6bBbiB1k@t|uFXzGX46mk}XL*1kFA4ddxH zwe#WL_Y2%_>{>gY#VOwmmFlw}h&fo2IalpUsWRm%rc8H)IPb zaG8d_j5w^y&a`7nJC~DLMmsL!npn#WhWxA~)pjOz`cv1bmKlAoPP4+)^qJIYquMsp zu25p5%YNP|b!n#EC+@WHP>VGfcrcn!hr3UjyI7={Jzlk$#oTASnm)_!!ui zwq40wuS(7#c2%X!u^X7dDu0e$Eo5rRfqA2iCm$}S%Y9~!T{+@Gs$Kl_lhZFxHsVn|7OBq#m4y#Ac``=262LYRf#dW{!Gqo}E*@1O-+d zGI4clJ}WVU=ZH%a*RKdR(2$Q`|L z;t+B_THMJ!L>O|JD`*5ZVMY}R*(f4#uY?y^fL%w$fnuJy#cdxh-Mg+%K@ z)-V_9|NO)>H15&65J0@uY0m|vP?kqO1d!fkVvT#<;L-2Ys7LKeZ5fUq%Ydc0_a>H0 zk|ji*X^cd6G3U<-E;J^VcLpc=*2bdc=3gfG(y20gF1NfG-}3YO8UFtN>7(`x^Apwo zF}rbyqQE!Kt|-eQ%R;60JZAR^SraU8;jE~S_poq{ihdlm+7LVd1G=IeU4G#P)%CqS88468FmCFPd1w_zTjh=#J(ae4;=fMT>He&p%8zE!3S5Z$9O;uM ztY0IRW!8-zAZvGv+5`{Eo1|7gVRtpRs&AgKAFvkPRZSMzEov{i>tAbK9IUk?SXBGT z_|a9-zQq+vjyFbE&4&CDF_%GB1Ji=u;)d#_MRpu!?dl>snXdiUB0KZJqj0t+x4{mY zV)4=7aU(_;4W8A{V&^ulgY3bB4Ok#G7hiWUS;N1FiQVT}?t+jc7sU>OVE;k*G5u{Utrs)x~z@PD^Qoj_J_>F9>XDHAl~EeXHW+ zxx&M`+&YFardD*oa2XJnimD3!aFln+(jZZ-=m1#<)5GJ^aMfjrJ=FK=Lf9x0$8Gs{ zm7NTI5Xh3H47gJW<5S0ytxrcuC%T5PGHE2Beuy)a?opGLtoZ`Hz3PzYfQM+d8RV8v!LwPxXj=16!X1g&Ok0sgtxC9gVXUW^E6NdTPiQHTG#(x!EP&o# z{GPK%D8vZQiVkQ7J>Rk*XzZn3YON)#lE^?F_7h~@zPASt?-9;Pgw>DQa9ul>qQbMF zzIQ6I2MFEJ>AfuALER#)QS^gM-JbkTGHoGw2;m`#tm0u8VY^Uhl!$-7cYg&Z>l-Zi z7a8(vBt*9h_Gdi-_A!6fH;9~(a^bHwY}LL9HeS(LUbAT-@9(p}&-6wg-zv~sisHJS z;pO5MDB!Y8!5A+XCTH~v0w)>S#cwAUzpHOSJ5wJl+{$I0?qOMhRcfJZaTjh`{;ln#ql@y&O)v?2w&i%?|qyHQfvPp5oYbR@v3Y8!L}Vu zh4ve0=kovjOTTmQfO7&3XKnnx+DjKzGnd;DCF{})rMuL}8DIW`+OXWN?W;gXbl#d> znU(5v)^hcnj+n!AGbfTlUi~IIK&IN#6?5tlY69%FK02VhAJ`RO=cI9*W{nQ0B7L3i zl#@s33ug4_=zvr|a4tYjU>FsS8RnV<6miA7BEoUHtt-5#f#!-Y9kELLSGZ%Cs}c2z z-|oYb10B4T^2!JeBzVl>G~UYcEhaZacm;Wq%2I}cbPBVI{;y#N#&3TbFqdip{%N8U6CS^JQ zNga@fnf0WV5s7diJgQQsm|FCdo$G`9I&}!uv2il9cyyOuE$T*lPJ1reQ;+|gTrlcj zh3a%m8qKUM+QX*>v>C3G53oO#U$xw=sa&~)s93t#+pNeqy^r#Yuocms#=uv7All!X zEb|DnCDNG%fI6#)DeP4krq;6;Q&wj)VWV#BQ?D4F-5-UXiqHr*X5VM)?FEtR{N$Ea`o4D zz#EI+n?qB_kD`WgItCjD343Hf@+7zeQXR&gI&#pp#WH|MREy_RfFA;++j+|P#lsj< z=Pm}_!~Fc|NX+dI&EdtWz?(N)@`M_J&B+dtqo&+_h?Tk>1`hu8A-43|4otYnuK1W zbftL5R|M-=4NsVBAnFbxdtPj3M`Yoy$m%Wt%F3(oVy^zq6-R_$WQ?J8SIfX|{RREt ze@NH`*YHO{K1sn zUt|boy;MRX4EK3A+F~z2FCEwG051#noeXzMXQa5unhLWl{}x~E9plpAd=33cgQB=z+qnMp@mOZ~v# z0sbmkx{_sxWT_u*EiX+LAA+xs#Oepoqc=}8Jk4)`(nNas1l{vD?{fy$b#mOznxWm0 z&6flw@#&C|j0GY;GEQ|fVgNQX>^n$P#Q<0{Ww@5HLo#eyPD!tk!MESZA-+moo#MU~ zxbBi9OqQ(L5OTb(z#Z#19U=2tGHYX`Jt0o53f1!tuj}RJ5y{m;U%Km$Z`ORm6$-W1(d(n>ORyjo;zWPuaKiHNqi_s`rgTujhs z=t~%(lteD`fc32(FQsqX>=y8(TR;o=|62n(qTYy3HEj#EUC7_>@VQAep-$4rLA4L- zZy)?pQJD?bgipu+o#?b|z9W&1@a|6$ohzU=!qlemZhGSV^cgyRI(AnOW;%s#tSbnP zuwh386Cf!9ok#{PMlrM-`bS&bj7-lhGFIpF|0A&=n?|HMcA;iApq0o3<5E9`?$&)! zv{l=+3-y^@)Nkqb(B}FB;Pn6c%tq?>#sID^B5xYq>38zgof>Vmcl&*P9~jf&b~Oqh z>c3I0Idr|{ah7nqCdO?>Y4_$>Qo*KfBDPJD_ptRvOUCM_Bmo<;soMRN8nWZI`iPK` zyq645v+iK%zVB}gSGy^1HC?>c2%M*&4?dwX~dcreo zyRkR$Y_us0`J3+N@NML82dk>jd#3 zdYy_kt3)G)kpS)z?f|T{J=*lU=MCsa4MLnj!uYSgigj``G1U2d+V)kDnJ=5^ZSfO@ zZUYK!kVXgJ{TQlwL|hJj5h6HDd$;ySP`kH*)0EIyBCB~|eZE0E(dCndIefLcw#ANY z%~H$O>GzslE4Q9U(&$@JqUX<$E;q`#7I&*yxoN<*NGITCZpdwICR(BzY_-ezR=S)v z_A@HXJ}8XV0+G7tU(IM^7~PgBywRc!MiWL#q$Lki36q5~GmGJ?rF7lPJ-NBb`rPrB z+v0Evi)#$4{Z8n#H?2kO%o;UB(YJ1P_}-T1)Jww-I(%>A%0Kwlwr;B)Bvi;Y-jg10 zSnV*h+jf&h`b;Z%MatN+(7CW~o7i3ruKcf?@{BF(DXhBqvhQX^n~C_cP3zMT`Yh)5 zd{;*jt2;Wru`k}x3`xH zIaVn!QO=t;og@ZLS1Fg?sP7J_V;H@cMVsNWCbWH~GPm2Yz9#$NNqb*ACa22HSJ7ti z440=D;OUUd6W4?}JY4|z7A)Kw&XpoKZbVqug;Q?5OiM3NxKbNS%XS3Vr^Tyo6+X+)N7!Xh+jT zp`dPWw-bHKN@|1qdnM|*(&%9rquWLQL}Rq10~*?^d$wA-uJ}(i+oajTt=7h#B6J&*nz|iO){>jAL ze>2gCOg-TtF!VkTJ+;y^vKE>mdM~%-EtxwBUPM5X&PRWRfVyC|oU@!eyE?pmicK8aCiMRtZX~6vb*=E>)G@3HC=h(@;2Pz@brAHE3a39 zoeI`Gy`XvG{Al!1dBP)a$ULy&2g!wF#{iQ76c|eoM#H}tbVh$0{bC?*o_@>AHf&BF z{I$r~Fdk0;(4FWv83T67k*`DHWC5Nr5z&=%O~fatd*Loa^@G_g@d ze+JlZl*LXl6{3jca@2ue!^n+(S2Ddzx7vLI5pLLpfXZQCcZu8N4(Wlp(qA?lBZ(fj z>p7Laix0kTz5r&IK zd4z`&iF_nKU(u&F+%-J!{#PB^Wv3T!=M=bmjJmnYjwvoii1K$osX})1k>U+YwAD}D zZI|a;%&Y^fhpMzH(IQ$y!dcHJVI8U0vo9caF7o4VA;cqH-+@-13d?f3q@@oV}ueUkzxJOQ;NOR>DBgokRX|CMTgb06g zt+@%lQF24{286U{vp+);IOY5q=mELaMCfZorK$9uvnqEF-~5f#bk__vCA2EGMh0 zd)TW!p(^iX*{O``wU>j%A!_knyGnd*Y82Whd85&%u_4VJy|1(%L(xo( zSH4vGxv@^A^*1)DbmLltOsCTI-AXrRkZ#-|dF-GeMsh<}=Nc~nCd(UMyol#IB+GJI zax*%+NU8B=!p3s)Hd%7>P}1iRr^v?7ae=(i?6%ML+$w5%Mp$&L-FWg*<7Q}$_~p;( z+pO*-@xqj*vcQ;2yFE7tAJJGTKZn`Op#~V`)NaY;Al>u~(3?`P1HU`#2dXeywz!pCE0pE(Z)DBoKs^Uin_lJvB*-&i1B-p2J4re3C+CplA`fwpbE;|x zqG`&gFUoLW*8L`Tl%ZL$o1*L8PWW$L?wc60NT$~t7%Go#h0Lw0PfXsm{;*XEsK!EUIt7Z7EhVq7~+ zF{Ri`yva{`A;Wyzno;Hs>AZ($suPFo-15=YoV=ah7Ir5uF=(8(T1T0yP2mc>iz@N3 zT|au6fF<#z@=@j!ZsPN?YTRMFnXj8zHa-7*{@u#5gC1p0PI0CjO&H6Y3`@-)H{Jk9 zqlg7=vX-y{%u!VVI*MpCc^x3HARA|UM48hQC^{Oz4}_bv{`1aMF3OzYRErkv&Wg{G zB$9IET@QvVnK*W5`TvFP^01FoF6T13ALGGoKs|A5-98F$QSz#h&)d}y=9=d@Pm!-q zJa0EK@mr6uA$v$A9Ki;DthyYrQ+$bEQ9-eHf&PD6GG%aZuO=VFzwHCls z@aVryk!sUQ08{z^tmOjR0Bk|e={+;bOpIdhwIQ|-4wFI|F60JUqIz+9R==oLno8o*ffvO7o*f|CV9(|SKj+9@z-B*+Uid`9d%Y(`GuoM+dU*y z_XTdkxKxZYZkwV`OIQ3@_K06=74?ivjc#M5uSvf@ejA%q>OY2g^s(fz`TpwVgxo{d=wY7 z)Eu5ppqxz^WtuLNNDvQu2+vBSw2!A(2qz_$$io%FW!$MoH#9vP8mpsxq#peh9om+r zU!#PnZMoP8mM~rng9-QUZDDqs3?u@V6+H*8XM)?tyKq^_({L-0OT2yLnCqs5VK4&uSv5Ua8$t4TVfqCKyBUUz*+`$j)-% zZ#?`#_+293(-)9yGd2dy!q0f1(>9Bi$YmZ-@Me{yLfi3Kl`sjJ6KA4p*scdIOuaoAMdV9W_Ak& zJXAsYN_qQA9V~zQ+wLYKBSiHscBEU*$pCJ zxSn>i---O{_q2nwtGxpi#MSP3+^%h=sU`AnkvenS&NHv5)Dw1HInpxt1n-oUREy=` z9(Ck|U7dg5oUrQ`bM6G#tg5}l>9cOC+e>!CtUW>UDqv}I4r)<;KAO|QQ`(%X$9rt| z$?8BJon6{|jFx3k5l}Pr>PvQN$)|$G^CEj`voWoI@+tMlON?rts5&R@tTM-pZg|Vt zrA?L-u(U;nEL#}6#73Q(tR6kdW&iStpXPF6IHn7J z9M8yND&>@&S@qMB;@6eFo=y6vCFKUE($|I4!IFD$5xE~;T>6~k{!7g|WhdlUc8EOh z5aEpG{L)8x=EW^C44csvm+;ThkGYk8dI6+(tA_LwjBsO?mwrr!fWsJk27*zg7Ymhf zJlo?-FA*vyknYP$nJ+Utnxv9nwkyVpO9knnpX6}bszT{$U?V8=bc`DCGR<3DJ^nIh z6dzCrUgjLv3iZ>=c3typm3i7O&A%3>?F#%GdYbQ}q^d_x+qv<1p|YM)-yA|$=iSx; z1I7^&t>zG7!Z_lh0YzX0|E zq=v6HFEotJbLyKZjACCs!CSp}hKF7q)<}l3S2qWb7*J>oYn-5;d1|lqD|p1Hd}COL zM4&s|!R~<%!>Hn@io-eybdQRC)y~PRQF6jKW99q`hIyt2rmou7l?ym4d8Rhc$un0j zVBnrxmgi#=S1zanyuO0A_ zL{IF>_4-+q`?bH+oY(B^@X;hZ(`@BIbWk6JdOc5_ea)^LIfw4>dNAKk!(E*lE;(L5 zr=nlCyIHT_Q6pZrC&tc)leagm99fm}cy^4MdB%<{IVNZv3Y%m8fx4WX zpthaih(Uj7bnla6m52m3U44JXE+0R&9WS%IoMY~kygvs?-^nreaZ2Ir&#LNKj+6$e z-e>K&+Mnp_MRLrG;wjK#lsoS*Rg+W5Z*c2A8Xj)cB-`&K4%(dahcxFKoz$+gc4p~L z8h`E|Bo+H3=R-MT_r@dYzh~`q>y2Mk(i?Ve>>Ix#$0r}lIY)cF@n=}@2qWi(Bu!z; z8+N6PwBUgQ3XGf;$l_WY&x4JerRrmt1sezbFBLDoSjj2gS?cg%oq#@r;|Ig%UU+= z8AytU{8pgKebXM)?Wws2rzJ&?-41jcUGKf-(4A%tMQgg!U4li?m>B)q=$#^8y2Si!p=|n7oqx87bdQa>mkkA>M-F zv&AgNou`;6)!>EdLOo>jNemwxOL1-CD3P^CzKWMNNs5~+L@`P&o+K=kNC6KFxGi={ zqz?~A2#2M(NfdWsrc>O7g+NaePEp*dX8$P8*)WbbmChwb)_aK1x#JiXTk15WJKdVN|T0g(o%`_jgg?Gx5PcGVMag}hT0+Cr7fq_(` z{q40M;`TkL``Qmqn}fz_2YIil#TSInAumqOsz6x4^t~fvJ}aZU^XB;`(qylb zjCQx|D3KR=c!$MzTC=mX=0&$PKLNUcFi(`H+do>9Z9Wz}0pkv{QFHYj zJ1I*lF-f|~g|<#5dL}uQXt@F+OPxx{tqr;o`+#j%>F4dTgD&v+maB+Qi-9vrKAF}V z-Y--a25I1yUlBf&2#VMWXQP!V5frf%PC~2F!upf{Qp8q>t5q7TKg|MJjc#iHc{{=` z(CnT)&serfeS4mPV3jI$!A>`Cs)iR>D66X$%D>U->IEi=TUF@09JJr6D!*%&59$wN zdceNeQFVWpJEuEhR?dyB7qAYibg`Ry{9QY{Oe1Y->IFQ*;ggHK-1B>&a4_RyKlRbO zjK{Gm;G*63UT*H~klcMQ+A(b|{_JL-IF3aXPsI&bt<^Tt1;cwyi0aU?1P}cCr%;g)-(64 z?Rz+dm+Z<}-57Ybo#obdSbT4<-YP9HTgHg9AsH9yjNB4-KjS);_?}%Yrm4I`QgBgQ z>0(fyQ}8}x#^t7J(0le!^RPPqo?Rwoqb3+>ZTAzr%v~685^3$YNAU8KD&jKBE%B<+ zWo}c5SG_LV9ZM&O6f4!LV;GCFA}!``hWUPXbsP*|d=PJ)MOyuwe%9JB9xD}T;>t5Z z6d5mU#yyJ>mH#BTvh-1sGRYf{=FuTr)uw%-KZrDU@xRgw0BEOHc}b&3jx{39-9nP? z+-e$OQWd=U-y+RD%#A(?_AFfx0ASIcNY(v)ULf28#$sr^@j|4zucVW$b)aFi_9hJ) zE421Sx#(Hpg9Szgq2D@@kRUH5N1FSkOgL-J$kRGYyV8;7i!LLr;jVRWXE^fj z7vE&fAk1d`yE;Yv_`aR$TL#YKP#$(O(h3$aFuJsUnQ#zbyGD^#hzo3kF1G#^;PR1? zR!KiF%P`thNHENs9~BwLF3DoM{2DQl7K@1H2YK}Bn?Hd=pKmi(B46_`i{Pukk-xP? zxq{EGgw7QjSymS0pQiXW3ki=&gc`S5MNkrXmIs8_W)o)M*fjv}sE@ALF}Z<;=S|j= zKA4M17GXPsBHv6p7v&y6JQhiWVflln#A>sAgZ)2N-|(#83+#-_{=lxpRaI8Q_C!B* zOu9@oGX?KB%RVw){6KaxWhiLhUxQJNFX|fR)g-n212!02IapV7T&xbdkmIl^nXh*A zXI>zgPXxH&!G7>T8CXu}r4~QvBb12F^qpaTSOL{vb~VysH=fCg5Xg54`1hmRcSsYy zk+h^@$3-GnWn23HEs+y59f@4M4vH(06Kfoad>Eo;US+TT#6A(p-^Jm7Yxih78NB($ zhDMq%GkSlx!X3TaFch@e((OioB+a zyyg`79z}j6??H>h(B?J5EB+#pIK&0-??qnIMLN*?Me4Y}NCvJp?~siWwMP41#EU30 zQ`w|>l=LU#QQy}EhBu}bbt8`h}83!-VdgbADw#PreS{b4#hVV zh0(_?oK+R)K;2PxeZJmL@*rjY$Bquo)qD6ynd@liLV=d`Rd&3v|Ez0e~sGi*nna??NgcREXsuyu5oTf$%eM>?-xh~xLM9BK;pl$fq7bM(AJ_2X={?>}}_ z^P_hiU-08DVDU!yWdxv|!JAUXi>?LucoeDH0&rr^e+cG9?0{79o1A1e(&ws4RO88|O#rW%c*t8Mi(e zgf*9eXnGSFh=RtC7>(WM>q-(CI3l0OOfZbYj-59f?bKXfG3qcUY#C{emopxp%yNm0 zXBU~#j6w!|6=`;paZwlYhARIFtLO3m7%aG8>_Nj=+$Yk^p{vglcTk_d{`4`%Mi~Zk zpd-UzTRf1qT~eK4FegK}0kD>^G)b0OB29U@K?APnhOzs}NE(_7;^Fv~Gxkpt59jkqsT$9~?_{cMw5v{#osf+^L6=6vuV`0D=<)WsXeO|?L#qDGnX6e z-KmVv>}cQ2^->#=i?aqiiIUiJoSDu4P9#0#vJmI8(7qf*;{7c2)GX|cG|R!l=dD~8 z+9Qqj8GaV1b$hx0;%Q-_4WX5=zyK`Uhc)EhMlqLS5uF#TB(>c?nL<;fp?qHnSpD)D zgKK}9LSJrn{nK=5itjQE-4gA4Q$p8Dx+T7=O+G395yEsB4;vbJOLu#xel@;vFk6KD_fCVJ8#k{}lg++5ECgGz@q`eD#yGTb~(jP~o$R3h*2 zFqn|<4oT3)hY4CAS=n*&s**N_FiHdqCmm)GwP`BM zW#8*|`Ix#gfrz|Z%|K!(2vXri4vd*n*Ex(J^RL3}+ ztj%4mVIq2%=xBrR3)rE?zreZPe5+*!U~pvA-1XcH}VB zO_NywY%Va2uH3UDHn$^<-*Jc2_#*^H<9DR3Ivz?MTsY2%94Ro`qvI=tljNP)DRPv+ zP7wcq@P4wk*Lq1ySVa#yewIAQ$M!(vd_Vt10KdmFi(z)Iq?y9482Z|l87x53JZJ+B z;8S6Sc_YTP%N_5M>o?afgMeMuXn(WI#IegR^qyUoIA^R~!`!JD-4_As;d)xGWB+c{ z{$Kltg`U>UvA{R<;bZ@C3c4faQIZ*p#X#Sf`mfwpAkO*_&O{2*crpETz~T%cX4>%+ zreU|mJGrs%-&rv;l54w5`m$5>9Q?1M{*QFTD*3;v^?#(} zZ0PiV*Od1%;m2o8Z{`*-;ok%tcWFAJ(gariJKd?!bXQhi9sWPk8SHfa%l}RfOf&+9 z6dBW(y9hu34}yFh*_gi1Vesp+|C>%@1U&4X{+up;+k4>Z1v}kD)8BNn_E2wqg|Mf8 zVG>?YE1PzQ{&FlB|`N(WBofXkm|2-tKWGQkY!Hw8Af4+ z>JPMbM)s4JqHi){cBaamb(N(sOi*WCK^JFI=r>G6B4=?=W}yy1CUPNg^u<4$4nYcvLZGJdm2_kfYI+y@`V zO6V}g8Bd9LZ;f`uyPUqF#rq0;9CO6GJl&Pdm%u)A#H+7J5b^#=kg1C7@Hi*2ru6iS zW0&-gfRt5K_OriI% z<^IdpMJ3geTXtqh4+ohWrv>?~TXyBl=MdMUT~qQSVPa*lY*eQVrjP2S4NDG3oS-e16e z1lDnFNsFJqvekS5$1X@okUrW7z(a)Y|Ll*$(dsai;m7Uzlza*k4+nQ4xbq~j z2Z`g7Q%W0j`*bn;x*yqU<(E|0u6dB!$2;WQj^9}O{xiO>0nH1h~(TNDl==LWXaf&gs z`PzG3&YDRAr>*66e3v?6K90cjV{{bin|(}Z5|*iu+jfjM`%OLJ zu6mm_yj&)(X;SFV+37lCj|h2&5E|TlE(NstJfcT=mk}v=kHbh0*R*!O22Av^)`o~) z?#T5(31;m^vV3$c;z5C}Bo567#Fr>@AfmS)uW2`c4TeoLBDkU1{J9SN4wF@}2<>fr z%;_@={BrO?)(ehe+nF{m_4Tnn{=<=O8r2a><(F+=wQnpm@kyY%ua+P*_f#bv{(5yKbQWq$Txz}g1L3@tUGo;t{TX@W7jczsnvJb$?T;L-QmDXq5AI~JJMXMyuWhz@3Lz6EBE|` zs)@gX*+cFBmE-3})SX}L{@g^;>oH_(SLi*RaNiWi6wXPxC7;^%pDUYC#YU`xu>1( zSX?^3WnLxc<~iZs33TkQo&fb2;UH^j`}v~x)A(z&z%$|AsSMA5G)7SSSCf8?@UcV| z^Kg^!je&_A`5rk=;upv-eJ$KOwb~}lM!e%lX^kL0@7BLJrE~B?>Yq|%J`8nJcO9!5 zu=H)XcZ(qH@z?JUkbV~=YY*i6Xr$kuRGMy|1d>IU^z~tA>Eo3ct$p7C&5~xt1t~ha}Bg7p-i~<*$|MP6{hJreby3I zOJorb2MBxl0LR8$#^+GKh&JllC!~dY_f;Y5JAV2f_&|SO7toSCqcs;f;oiN-$TWrK zAP}w-IFMsmy?D4q_)gQMh8v&KIhuug4`q=fMd<#Qm&@o{eRbC|uX1%A1{=N~04WHh zZ9*v#gb;#{FC17_gn-c7z6k_bDl~uPA&bDFAj`xY^leONK+7aNfL2-69IfK|C!=pq z!UHtHrZlGsoDp664O>T!(b>e#4G67(#Dsf~i?1n0r|&=`+31_+TAjY5fQ&F?11$Sc zxOV_Nb3&KGTB@n0cX;gB(D8*vxVIOF1uULN$#VssC$6--2_<)g7UW5CJNkmPL#0~Y zJllb3N5dEgJiMgtsTD_yd@*U?QaE7M`jSIPh`FN)bhq~ z;p}ggw{3hS@T%<%xA)aV%o1txUAVoU1`SA4{XO7SSCc*76v(dkcx#n>35>Gq!tG_{ zpcJ95d%RQSe#roDSu5Z#H8a2)XO>s11H9!KElvb@vl$qE4DdGQAW==5e~cuyx1rfa z9k#t0<{SE=u}hd$K`Iuhk&M1L+kGbi>m+!YF*4*v44kM(3Y2*tm56E zJTExh^L?UQ-0Ki|31F)~0J>giU(~$s*8o#yg?n!CUwG(;|J+aB5_--8nB`>ehYt3u zq24a$t>eQO_UofR-WTq#z-;5s1BMl- zrv=&>)y^Y(fuqH_>=jBW5@84PAy7jqQALN&gmH?t&u zVBTZ!U#Up*|6ECLO>Ue1swCX>Q>9B`r9My%N_lG&d$^Q$TBY@d6*`jEG1$m$&G?)e zZq?B>D>BN=1bki?4jZRpLcOVF=}CFGb9pQD>4b2rNe~m{egMu9&Z_>Q-qJoAAn!ez zE~#v|)dGP@`T*u=1402VdNAB-!+0kX*8z!y1b};=3b)z@0~`#1N)O0PVg#`|l1cX1 z2T;oa&HEZw?3?g(MaEET2sz?W;@JO?#?U+Axo zz`VBsrT|15Y;hO^{wU71@ZA8?(t}sd2o5(tp-SO@BC+zP!_8}IQn)wCd|y2s?oBKX z&Okca4SN;ad_X3T7kKu*YqFhpkWc-EMGPz#>$o2M1|;H zX`aD;iisJ>_&+cY7E62^Zl0;-tWpfz4sbodS6TsV=G1WDy8zz+Sg~`sc~%sO8s_tF z;OzwH)+sV-b`oR6uXumvTNtBA?iaA4xU=2Jr@8ZMNUR4Bl9M9@RunDHj<}I8M+j=- zvfU0<-$!_3d}E335(NmD7;e53DyYwb){D?pB6xuLG<`lBCRWrE9`GCUWkL`&mxrB% zEwom^M1&gkJh8V4XC(3p58o1QNaO-1e_%HMu0%d2QjWk?eU>i@oI!9psIw&9;o)Jz zs6@kxwr67sq8S(i=8+c7oMO;Y!V?lf83t`3Y?4S-9!?XE8)BHLGZYkxEai#3N0VM1=cL>6x5CpHIcMgI!%ldzN!^_n|$LX60j%Se1atfE6#>s zLqhGkQqCUAiKBjlA0TuROzkTc|1#V>>F+jCb+k?7WjSpVkCyh93mNNxj-+bPm!-XF zAvq4{VFy$$ijFo;b&c}2<(mTQqP!J-4fGn%$w~gGh=>nI7bG%-3LAa_|$5%`?+6OJdUoZzXI5b?kz0GqX5CI1Jzf;RwLV z4FRGJ8c0(Pz6@}GWVqQ=Fj4d&kQpK-aq8A^Q`YjrP69|Klz&);6SE&}Yx`Dias#|x+Q$RY*U;F?v7yzD>b5dqUEC$#{zI7V`@VB^;w| z_nkL7aW7l}{Hw(=-ty5^`$IGXr>+ib?N!3fAWq*|v6I!A7;k5DvxjKALkD@!0nv2MMx%ibpiMI#^QWi-fm;(cWXq z@KV`c;`E~-MZo$#A3g?~C3pqVgs^h*PG|UJw;2mCO9ir~+Vwx-Q~j{U47~*n02cNS zpB>~VK|ya|y^_Zp;d607;(--SF+=(`5`AE}w6#@^W}ne_x81edo763vVtcoz*9C-qQ6W;eEK4#35?)>@(q6 zNsa~;(2xaFfUQ{;o()X&uV5Qt9p#o`w~TW_nepBVAtg%=U_E$HAw&~Os{Zlb_92}d zP#*3xpri5L?2sJ}s6E3apx@)Yt;&|<@wH%`nfFit%gsZxiVv#CT)a$0m6zash>HeJ zC3ugRN=+%lzfLNvtoKMpVdxkm-_tYBu*yxA^?HQQQw$Foji|U)t`Ji;=cRm4O(>Py zqvFaj-MXpTl=C()KT`|Jd7o#cFE^3cXf-3z`$UKl%DWu-mM~Y|P%%k3)V8XhhuOz0a2{=E4>*=L9yjg12hPc`mFfB>+2E!8^kDmSH7tK|dvbB`RLwEn^~+ z@fP`JjC5fHko=CDEe15 z|8ct2?5Ktr1K9zIY5c3%2oI=l;{m)1 zVpa00^%cGKD!!##y79xhrHdIxv#`b!7`Q5Vc^*@$@kHKjs}!^{#kO_U&@|(A>l}XypsoG1(la0xv%j7SLKMz_T+^uYBR36*$Ns zGR6oCXh)0qv`P7%!f*Pk#q5=8y%ZL}1;bWlSy9*3`W2jC+-emv!WMe`)mo^lweWtm zDzn6;YgNdKLScl|VENx`;MQsZFrWW?!2N1r8;6r)pHxd$9wJ7B1(cyek)0SncZm_H z+Ojh4&OX5I7*be?a>^wY4x&~m5rROx9ii33v~E~HBf77h!SIQTmsY>XES=1G8U5=s zwuc2&$B{{0gXfTj6IVE%FqW#P_26L^K~{#-@_ATJcv2!`c)&p_+#rz|JRB#yh@RO! zAgfp75?68{?6?6pgjsPsWuRq>;n4KVL$ zkvdVu8|$OQ#BJp0foVxwsEM#Bw{b6^k$ximH8|Vw41X5zY{1TPjaj;#p!(4`v#YPh zm6gV-cPN&|hWdf90Pa7vQoj``V_}9e^b9R7RX$M0sB5J~&^WAqmcC?oJ}oZXe)V^v z#~?v!)P(?IMK*=&P_9c!ow1BIyV1hf+qiX8eh;fC#Kk)fJrnXg^J45V6@UY?oVD&np_d z4}{0SJn4!?_^j-rzVAmZl9d-Tl7UU6TUFMEq%ZeM)b(p;@WE&CGt#byd1zYLPgEyKr)H*k%lR-RL$@}=dA(~`)mkQvkY3|C8!hxn5z!oE7kjs6 z+hw^;PwXR8BrI*U!H~{)C(Lu1{tj2h&{qg2MFs3tWNXIKwGkQ=<~fggr?YNr*#|)Q zhVZ3CPWs>sl%aoS5!;GZga%1RcnfecAS}RM1~hY3WQBl2re6 zZ)|K5tMuv4H@d>^f7HOr2vbj{d((pxc2U0qY2A!5>dka-st0QD+_55dQtc=ru#=I8hsgGxr zIgASstc=V5i}m&2KtuF#9V_Fm3u|(ZSY|0eFWw6Cw1FWs4Pu$){}-#~|AJb%pf2}m z^;REccvHGfje%4~I|H44d|{ZUiWiz+P|NXKh}3o?yZ3~7Qp-xjt#X>b%9$^_l&jsR z{GNIs)0>?u#}cX^4D*z&=2AJWh%jg4jz>puiGC{8Yq?aIqdBLQtm>`R8=2lzbF%s= z)B9krBoG?phFPC6)5(xXZo4q+w)8E&N@rF7-pOx$elLFw6A*&8uHO@UjmZDSs-(78 z_g0JS?xe`}gIj)$0`+xuZ@RDkZOTX%^{8>4em?H$Fl!@SM*8`%u>_e8c77tv+N^m2 zFrV-UirfHmSmQj3E8aaPe^?jn6lN{cetVHoANRp{MaX6?a>?qC*a=w%zF|uoV`FO7VHND3xMs2{AFl)Z%6}8XWtunH_Wtn$%$nwUTrYgu{ zedvO8oVikkn(!JD@;RHZwfWA~U@)`gv(6H+OqaOlTm&{alxDD_x z&h}ln0stpE`)vg3`#H=SNWU(RFo!1+$|e}*@O-fD%4!k6;!A~DgQ-sT(^;gAF)%t8 zFbKnD0oo(>g2flAcHuV8k&KsCU?=~;T_lsOanMLGtXiv!jCTX&K(G8VO|o#eteOE> z%34P$nd#Q>DiYbvLoGtKyoAElZyu}G8MP+c8|T9`8}b&4Rr6J9Cvsr-P0L|;b22mn zC~|nfmBVlx>fwC>iX0yB%K_JC_*9plSH}xK@+J$y1%!c=Y6W9@rFnUgPJ8dQFxFdxjgqQk6aE001+^UZRM&ZU zjd8g&@OXq2&VM;Y_})5Twfe?Nuo2;;JPeX=avAj(+MPDi>e~1Va!~FVIU<}j!A1h& zsecibmP2yyT?JI23lH~zJJc>1&rzL}d+!lr-+K?#s>QKE>a3hmU(U+u9Yl!77RgaL z=>~pAXKB%nOYL+M+R<=8^-j#`7VF(z6ahen>@5Jr0%|(v^4y`)b-? zA6BV@^&bRI%8|h9*+Yu=)JuKA`j2AA5$NE?fV$Ip5u;*re3b_3>PFuNqSlM> zzZ7ntVRU0ss`Fgp;k23!YZgeT*_~khcPiJYK&p9%r&bx1u>}w(fwerO z18EFmtx)ZN6407ni8=%t{TlL-1Ta-Yz5z~f-btikqQ_9z3En_&Y*f|}#uG>$*}@oF z8l6yg-KqqvV3VL#@LHtTh@%TidP8eM6Xhhp4h(H7_4bAVWZ zyKvytOYi0)8#i6y{H4)NoL);PqbQ;X8G|Qn(Maxml(uLY`b=9aiL~2dNn5Ke8eP$A zi6P`&4r@+(MsclUb5#s5Qi5FERZdx&GaIpEy*CG&A)cm@IG#>FDkWIoj9IMnIhA@7 zc)iA;k%Lk8%}l{X*@w;pW>i<^LEq^o$rzTxhC;Hllx5V1?t^5f+5-;OKemU?p=Usz zrdoRoG}kYW>0(kXqp5QIq;MVbsa@;4tShQL+kK3%iF2UeN7FWD3+(x`LO()@W?!u4 zTC)n=V>9=8cZnu94#*br=wtCnY$H6%@nV^k zOs+GOWXNeBdc0e|$+F2@hn`^7N;T!rr-;4Lz&(+8wIAP$=aaw^C(9YMBM`Dw$lK(4hlYoMc9 z6;O&Ww1(zUzpRV}>o^uh)4>dst(%bM6E3nEmMfutgwV5LrzOyhhi`#P5@^rE@4y`i z#ItgbaUL!+qt=6YKn}xeh;F7>y4kd&d8moITsun}k>lP)mL|wN-{`=!^ldazDV<1F z2&j9rbX#>ql5^(<>kRHnl1-1Bm7C&JjTc8TZAP%(K}8g3dk~N%x1>L9xpkm$BDdks z19cG*jh4EXyw2H5xw^2nG*@eCntk<_?*X%`g zNgoKMP&RE7ixzQC7#pm|_)tAQAG3(jQd66n&C-!a-qv7iBc`de+KR0-)1n?y2nu9om2d<-~* zb|c?k9L?Z_vp4uwNp?a=IpQYpJ?qxe$@ViWsbA@2vf|calr%eiY{Wf4^&Ps{2n^MT zza)k(mf{PzB|sNTfon>z1Sot;3=m~^u@vN-(gsQG&qP&o#yWH z?)`ZTkQL2<7OXPu%W*{0@|q=@QwdfdNqG&XFChl{$SL$jnj~ck@P3RMv`)3Paht%c zu|)G_3|S+Zl&=8R%0)Eio)Jy(1zR#HR5|4iZzmb8=|}tSi)i>2(VVAJEx(j%(u-0a z0S_cVFB*w7Mk;C0i$*>NC~44(Muq|vgp6J^vLPV8t@NUiH2uhzL^PV!i(1NR7(Wl`O@by!UQ zk*uGM!dlDFH*zDe+DvcdE?fE}kDtm5iM6wWd#W)P-mS-)M_akO>tCB?Tl24uIjpt2 zPy6!Hsnt;2X0%ngFueK_H{kFCa6M*33Ymn#OW_`Z`(1WMrsp=?C_5vQr!-tHC2zCb zEZ@dmsW@7rZbmcOTs2#^aW_tAfmv$Rx`}>Vs=@*YFKac?&zBJ`WtP)6l<1h3n&_Mu zwih7UCYU*G+%*FJMa;VBCi-v|bld*RyxqnfUCp$qIl%8wZ~ot?!)BAV?rP=Q{z=rK z8BO#h_GBc45?q7XpQg7htDFhu^0w}JyqS5ntvgPiYx=Zft8AAU)sCZ=#mr9a+})T- zZfNI@5A@X#drjZUyaTSWjA#DT&RyGU*0QE|*0m|)?#p`IYo$7Jd-|%%gKd);=TDZ( zv3NmVsQJcKd2j^vWn3Lq0tkqaJJTxzRDvwl)bltsCXRiADgJz?HNjh#Ujz$7jDC}+_}oHRJ59L z)O#?H-HgqHE5E`&q@so0FAk&Hts2uAL<t{=*qWWV zq@z1j-(rS!M7OG$Z92M}6c6x!Q>)aAH=E}kHs^PAmySDJlx3ykl}3rS(jcf@&x)0} z*hMosb;lS~YOZQ8-#o9>(n@x*qq|ahv8(yhD#cN36oyolYE}wWsXjM@JGm3RbtFZN z(vp$9q|@fZN~LLpS}mdPAYyy5S`6IfY>o^PqhA2puqaS#j^)d* zXRs|JoTmGq$n0=Dbo_-@5qdLr(U$gso7(>9?5$l&)3fV?%g_wY&JT#tJ0jT5@J zM6731lF^Kvqu;1cpflJh6zUs^K^sP6yGM_Z!0SAu0uv?h4i9sIH1;``=4tXqp7ElZ zHjPsA=Fu|Y(b*Qzy*qajrH;>zk{N)$^s#1exuryBsyFkD7w<@u7Goj&6w4 z(wPQU{!rKTw6MO65$4%<_aL@d;sbAa4DV$qSLfOy3D1=X*6hi{8w!mw*{+c^0P~R4#`iR{N%H2rw?kc5*D$OmxvH=(CEBz z1M{yz-DP2~3$JS|Ws67sVvM~JdLu}>_@$#sUFx{m~h>G$+O%CL1Y4pYD(-C_39Ue;gMW{`sm&OLX zrA7Q>=5=;g)MuMNb#}M%Zt2VWQI8^YH`>Y!5fj$sV^E>XK-DTleGTgM#SwZ@{0*Dx zrcG7sDe6y9&m~0YAu<{(UN8B9>J|j5ej%zNs77fKdI_OQvdCh=rM?O7DOn3pt?xzX zr5Ge^X5B!&vlmoh!CrdD{Iv@%>NI>!%+oRi`qJ1yex}{kx%i=2#Brov3ZogDEFH^S zkL}3_RVXw+^xFtIRoAt(VCRFqkNwd???ns)`_jMY4x#sW%2+iWAS2^l$&oAQ^)g8+$5Ii!;ra3zF^y+n`aN*N#hDEG+SBN}z-@_Q zNEkx}d0UTyl|pF>Kl!AW>8$RdPkfn!N~M3cwzXbn9a@$e=x)w&C|Tyw%hZ&3iNkGE z&Jxy@m4zylkVUHAsD7DVa%>f?9Hi04j040sJ5rB_8Nd{7;yu!WabA!wf>;hLl|WA(vVipx z7{Egwa8Lpm{ILv$W4-9Jeq=IFkw7Izk=&I9dUzeBna4}2MbPdoxWglNGx1EZ#wJQ( zj*}{PAE5=nRO*%ciIX&y3Tt5(OX}57_U?X8(z6ieNz#i#y-(7A0;=6#{QQY7mF*zQ zh)67aDg!*ed$*57Nz=*oH~f$_hIHdg@7>&G>xZ8b*X*f!vyi67 zn($s0*X|s+x92u`lJtl06!A99kGjzot0>+r3Y{x5S*>Z~7_P?^Cl@X!QRgtar;u7^ z?z~JWXjT{Gct-cxHhQu;RS=qJqt`mrha{)aWGSkH%A;OxzB4n9;Y7xqBfAT{dgv(` zQux*M(31MC5Y2eCEdo4(@Ww>}RV8qphlW6X31C{r(JJE<*>N6d@^P<9`I$wGdmDID zL`@GI_db9X)N`d)8-Ufg0=abQu!ow_V@44zTF<4+jXMGymf96q)#&lIQ)amWRSyeN z&hJ0n5m+3-Pz~=J23LhODmm_RAdmFy;V9|!IP_FHlD@6eLdv}pX-*z59^P0ONr_rA z*Wx9$S7p;Tw-wCJ0pAG2p%%keJvKw{Yf7+TI8j!c7jk@!JO>!mSe1Bdp~>@e%pv}; z>YisT-}%+vP90SXmbqc0M>*&W4X0W!TZVGY1!uXY1L$+YrEE=E3nyxg6{Tn^Uj^~8 zAS^>WMw24Z??_OYcSm!t6R*~kVL}H8O@+xydB_ztm-;@CdSMcL%+y!irM*5>pN{dG zJ-L>uSSpOeyuzBZJH3U9!wKV~&qoI^p)Vd+U91SQEgi88JJU=0ad>I=u3i-Fx&dIM zTrXbZ;W6+?0>5gSY~jCON&?>h@PzC8emwk|$0~mQ@Q4nt2EU$tg17@(S zvn81>_E@xj{TGMkLt z_R^5=uLc;&m2As07|MoL&qQRet}xZZY0gbSpk5SIQ`PNn3zgkmnn{2C_7-Lo`MabqcR8M4KF1T zWgeML6|*1yR(Q0~{3aridVB$#R9c0-kc#vY_ZXkhE1E_ZP&jrF-__*{NqX7wHcAdm zacqVzR*s%$@888!j`o`2ZV#6IK1~ZB9h>B#j0u#hm}6~2RLUk9(lF! zjUhgtBJ|fA3NIND2mb*d2Db5icn3lsLS@7Ef*yh=H+}&y2Mon=M}VFxLtXgQ5TBiBNblvs8B=Dsr;+>^aFAV1_ksdF%3Gm=#v4~E35rTWO-l{& zkxjohTsVrJGhF1X%fq+W1xE4rK7xua2*+C?#uFS0{XJ~i_yXWp2{0N@@Bqan(_57L zz3&Op;U<*gUE?a!?w0Wo#+o_rb$3zk7$Oiq2_A*`ETtyBn@Z|OG9gK>^!4IFhe(OO1Wux^ zRTfe6`{v*`+-18~$rmd~t%^zIEo)h&z_NqA8M|4|K=qX!C1q3{aT=gxPn~P%tbIpp ziu8*r*lKxuviG~@l{ehs-dH=x3`=&>TEImo4KFFH)k``xDqNL~tJ2NorDemtoFH%f zs9#MV6XLTYh?2XvQR<(8YoZqiDTAnm`%}o&;LR`Vu^~QvX_`LMBmqs%>kVDBiGhg1 z{XK0&>8dh1Na^}8`G3b4$GF7BSW+6zRpq6E!9c=X;PmhPl}@$*7_-V{aeORk>3EzJn`O`urf5>#PGYaBjTfS42tpx{!U;ut#iwenRqejB&A&BW#; zhYC$i6eL$_f{mW|n*^9Vd9fH?UvcxWJP}En(VywKo;w~adg%>gDwCTgO)piz-{&)N zM*l06)EFuI5tdR6u>771_}Oc{GKi~c<043+&TzoBN`~CN1)Z^zrmH?kA_|dWTdV~5 z_Oc3(k?*a4Ro)OV;X3e9gu0vOHN!Y#H3CQ6Wi|9wJ_h zxX9xA#1xdE62k~soUn-55t9(qgtd%9e{TfaONsbIG5s&l@3j#5o#5&39E8#t=)ggf zzmI}N?~`vz%x7Se0p55QCVx`H`Kk?mFRs6clQrmX%7IlTVp#Ob zyMj+D^Y9_CRsuD7I0Wp4-;;I)dJQ$sL_SwsZ%OpPPlX@DyEh*=3%|(cKzpC`BX|Rl zIqnl_;@f~2k0)b!cmVuI0*t4rZU8^uc#aCF%rn-fqtLbTmm>m zse^z4oG8-2FVJh^Nt1a@ah>HJ?F^Yd?6LfG{?^aI5H^2=66@rfQ%$nBk}QWl!SwVQ%Z!p2PU<=rrR(wWo-|3m%a z92XazcuO|+HZ6vT#+g(RU8^}$Zdygxc+Xu_vu1{+ns40}{rfN7j1*0;nT@KkVI*qH zABwkamTFC`Ph_v+--hwJavMssjgfm&G=Mwq+Qyh}1>jJlB@3Ox!Q~eunlJyJGU_b4 z8abf&waPH^e&BFw2m|J%7N^z(S_&ryB*n?_Z3!d8H}xr>K8nxi;@3mrbIxJ&GAX+_ zZ2Ay*L)aw$Z#Gs#dG#9WbFGTBrHBL5Y)TFJ_ zx-#{taYI^{UuoR}dgXxcXkD55OgaGUiy$yb5uj{}?3t+#Vf-;0TR8Kv!sI41pGn6g z|4(4=R4f|RqGI!kKcr&sgg6!Z3No37sES=7aET#NNg_H&Dki^FO!tvgEGAgu_!+{V zoK$qNNxwO%YPMBs+ZL}}l6fTAL^~OR)+R}>l+5IH#mU7All73yYVs?o#GiUcZj~3= zPc8$La#BSQ2*a?cBG&8!y%xE=xGomw9aSPp5d(N@I!PK4Y9O}o2`jNR_bcVvSX?hd zl6N_^X*~G}0<6m9)?y2~rZvGvzg)hh%iou{?EX zFm!Q__}ftSkj#C*qeu5NkeNhrVnXKF0|%+7M+(OR#;WV zuJ91}Lm2)|vT}6ZsT6Lip9EU-Ku?)kRao>Z&||WgYw@Oknd-VfL;u$(%6L-#e1RV8 zBQ3-)o6yxcs%;GBM3bxhzhHC<_#CgL{O{IWk(c?ss<#aORoeMkUsR3vu_(vW@m~0o zd29o*Nc?Kr(CR-SwIM$U|I+Z5N}Z#2WF)Ui3}KnZRh}+C^*4Zp z375Bg65jwV1{9U34m^|qLL#tjzmkweboER6>I)q6H?1-x70Ff1-!vL@S`9IOuJUg~ zy9iRu-w;u!^5i=W6F99d)1mSyw)u&0M0WGP{&ngMl?o)=@UE)1s_PA`^dXiM|x5O=9!z3gCr^M(Y+ z*ulx>KSQ`-rZ$~ZW>rI%NK5#tE?)a*Hi}w-%NE=?)i+10b>Z6giNQ%4DjVZ^taRGZ zZYf8sW=DLh*c_FP9J^m|5*|&B#TJ#jNr`cM)Gt%=>bjTiVGNszNXz|=xS9h=5}=u; zbpqst${5;$vv9_+rjnm;u1ux<=F!QCO0}{b_L7uGtschUTW)54GE8pi*5!NidQ65* zHAYaeQnr}&R@r*1y=iD_J=VAbon*4bVcT!tP?ehLrL2Vy+bc0PF42A*Z;;t8jvt|}Waz)z2-7!$;K^w6Ty)&i@9BL^;Ap2ma@0J|m7o`-Y5SHc4I zkHfEkaHgMLjdG0dE-X@rzXN!02@h6XU7l2e*vV;sN?;BL!kErYcS#_FhhQKOyX8?? zd_*W5C@0a!d1wJN<=RybmONf=JM~-!!_Ypx2gWSG0~Q-ZR@?mk!k-=eJ%tFU!E|jp z-Eq3ib^wzH1$-facX;?7xWQU?7VdU@ z9aY~?{SJ{+N^S8oXr1X)clxae%Lk6fR6*E4d6WTt8bsjXa>@b(?JkkeF8^C$auO$a zIwG3>Cj^eK9`CDI@vHgx?JP@mn4p(+)>B-%$?rf~LKxm`2rK*#+;$_t*+ltAOctfEvReo3@n z63OIHl@BR;XSB!o?|Mrg1if<8;%iFJ;ulNkcL4g4b!|^=#@q0HlO7r$YkO*jFSBTw zvq<53sC@V(-C;@hu2VRaZN_k?aH#|)ie(N+Arbvg(_lBMt+eDw;^qRg?Rijqvf{}v zWj}%^HuA7j_N9<1*GOdhA+TO5Ak(1up<>1Om4bXiry0eby_yEi*a_VZM+Jt8#rG<@ zXXZhKnFra}!g-JtwpC!pA=n&*y;!Sw@lxk2^Yf|WTFrba6iA`rmkOLjD5EAgLOB7G z<1V_4Ok8Gs1AJX5%3~@OzohzHQtfh5-GJ_z1SAobW5%zAiB73R{E~=%VT}5hlL(ca zAy+U+qM1H`QJBd4Sf%2ZR9}&5G{<(8O3g%JX9hZ{DiA2=q)PCYMDk0bvyw>W(Bl4c zQHVCPnv)2noEfhY;UFiJP>J{@(KnJP6*{jd;>?yn(o;QD`jil<==p{IqR{VgGI<^H zo=zqM2yiM1C6r8Rszm&f=!%@GQ2ZI>W{z<9tJ!NH9!Ns0R+Ro%CExEKT%xh!JOm~| zIUo2yj1WtZuD}k2Rsu^&AJ_vY$=1yTY6lN~$?9z^&6uuEc6%TfGbufI=BL2XLdhkn zMEs(g@_MFsOd6?_=uSg;N&?bKSAomUG&`X!^jei4{oW_NX4^Y$W$(0 z0weiTmrn{MOFWD!3=ma*^SfIK;k`vUWQiv&45%!D50odt1gg$$kb{=!hMwprs(-vkG+~8^<$){qh=%gQa`U>mdHuGOz8x(k^oi)R4dHt zK%l)-!8-mT75UYC{5DA<84FY*Ovo(pYNY9Ay$SR$OjJT8;+I65CDFf~M8lyQ>e$~| zxMQ>47biauve_ad^4n+>hstaogXI+iLZh@r|Uh)3eq(GGLsWwL>~p>@hX-JO(C+#RqX=#XS;FdKUB8 zSy6|?mG;NyRwwJlmG+<*vrzO|jF7^(LT7b=t?UF|y<^w0Vj1xxtCotGO8qDHpifmT zMXGKONbwVJ4f~Y65xTb06RU<-xgvCKIzx6YIcMi+8jG&lY+X`@lQR1hAqKnIfhY;- zm<*ldG}**zBA8e{a?`z(^)Foawp0{RHbsm7ifk!`t=LkeL`qp>=_S=hrz*0p9a%vh z%4n{M`{eb|@e#Y;hxa@J91fT|%im zGyr4=EDo1xc1Pd^2`uB`4M29? z)$&7pJCgY%ESCOoD)Cfi?oJ7yuyav@xpHK%8iT(6FrCofz;zU&{ut}hoz!Z&7tNoj znza5LLDG`-S#z@&`2pRws*wbkH_2ck9guiEILHoCX!boFoB=ot<=S*RQM*l2kp zS9GbjoE7ux4QQdYJ2WG8kAq*N$P;a}?1~F{AAdnN^tI7H*yuOZiL-{WHu{mn^f{pSG@$GI$uu)1&>AAxjh`%3ChPcREMR zryQl(9$g@ZY+$PdKIh>az>ys-is_U7=q4BGGzY>d?O&}5^hTW(kA@*dnPD1J+@$)e zJMo8s0}@E);Tzy92@G>$gkM97|JOuUx_71y=f7qXcMJHj0)eG)9wVb2znHy{qSf#P zC!ZG{TQqGx0-Dd(tp3-29vI=~v#6;5b)1KC0CzrH9A-*~Ys9bSA5t_B^D`OCZgHpr zU4+HKtIdxiR6A5yAgxBQ{<}HSl23Z+fBhqO;0t=Z=E|GxpdYsqG;(j$Rb&fmG5I2Q zu}zH_+t%{cHN6q1{X%+T^9|qEDTdH%!Uj==I;^~kmqek4DoTbwv5tCS47&9Wmt@p?fQ&@C~Mm0nQr_3X@(qq+k8nNp2eQB)Flub@i{tBj6QtSs3 z<&;8URpINMvN_4fC!AiNDs`#{CzZiH8~uilpc@RZ&{N$ux{vaF8{}Zi#)7hz$(Hc( zIQ;=;H>zz&F$0U0)>*WJsjL2=HSN+2kPDL@yv##C;5F$etiL`_CJ;^x@x4c?Y0GKN zhd&cfd(WRPfj@Xy3@j9W6Jha4^kyxP35!SE4hIWgZncY=7gF>b>N|~t>-r;BKIYTc z=5Lb#!>|<)9`n%HM^iAI@_)YF10SulPDJUCHbcwsJO3aOd>)UV6H$||`JJcH`GIiwnK$^O) zOe%`~WA*C?eGBx&I;u}*hWP#>TplpU&BuwIe}(fL z4;UM`M~ZB%^-vg zNdvk`bn+FAIJ^Mcoa&Jy^I2eftpXDN)p zC$988G?Eci+?AfA9?FPL2&M|gGFy@d#Y4Rf*o4Or#L86lC7Lngmc%51MO+I$=J=LT zc0ExWLkziTbH-N*AofU+HbPkTC`A7PX#U2e>qu34Ag3r{(g{2x^#bO#OEd=psL>)=?e z5fn=>by?4TYR9nMa#3MjW)jw~mK1FKS%v;L3nx$R(^?qhyoENs$q#ZVoCQ zCZ)XiR3udlQZQ9As=}?#e2o=7e)$VFaL|ldX=GJ3*xa4wj$l>Vs5(x9 zmRB6qxGQMT9;nxoB<==fejaG3qUclAj}i4N+8Sr?ebt-EjW@eNej~!9+heV4k!IRO zvnM_@t*Sl6s)yoY-HqAG3wD!12f*7_CTu(Cbxms$uT{So?2^L)vpb0&Jqe1wKyOiT z*0x+bFenQL0j%)2c9=ZilP(;owi0$!gh`U>@!al+Yh%ooiOh~VP)r6=B~XKh1;Bhh zO~$QKdK*$mXTMoCP8KZWMyZ7>0frvejxIdVB^Szx>*_Zp$F~XX2lh%`XueeI7~t$5 z%wDtIWqVazAx$I^hcvY{JjZ3XYcF+vtxhcMN;Axnf!IDsTZaPVv&b|Dqi+qs{`h(7 zb|q=j9##%>dYE6&c9*Ma``B|v+Qc1Jh{Z$zLOaK%U1kQ);YE--izGLR$g;ED_e{3? zEzO&A+R3(qxH)wm+cl_bo8LKio%lhLM_uU@vzUF!>mDAzB3ENaX>rw~G`-Yws;3j} zjAi|mYRcR#gZJ+$o|3s+^@0fGXqdIZx*0^l7Ulh{RuQ6k^Sh$QMLDEbNfSNhSJUc* z6p@+NT(NIO8zT8HfTMJRN>Os?(+rsFuIVjH!_Sc<{A&7*5FfsqV9XCBiQEgm@E&l7 z&1!d@0=-!;^w`aiJX0sL;uMTDtZz$7ZAFOB6)gS-$Dt`+#TbC1Am?<`d$2fkn~}USqbtmsfu+B&s8OB8x(RAT{TgbWKqb^{bL~ zVr}k&bSK#%;pHT&NV4*3I@J_wxW5k7Pokpl-M5<4Z190QwBH7hQTRSujUFLBH#lTG zuN?!I9|OmtD1h0V;kSl@lBLeFnxghSo0YJ}qIu$;8tGN^lBm152?Jy-p=ixwRndmdb4Nudt7FA5 zt@v2Z{8F@NQWaj`+xMzcQTi;S)u;2^k=|qIQ5~6^)fgTj8n*a5DZRXe5%V?W>ko*F z5Oa-(p}-)FWy~!|_Q6|JHl_gVRA920vBjwcdh=AVKs7!Mc5xxOv2ZYUWbs@G76a+R z{Zm?XF@D*i%}mo5Wl3lwp^tzA@L0kdjLntbQsdu6bU*S~Fclt4))IdbI4(T?O{yi^ z31OWV@nVE)$w5t9jPtQLQ39Xwa38oVfwNpjI|Udb0V=YD?y%&71b$`kGS>lVECreD z=8!M#8XZ<-7@p9A24riN;#LACRvBSrO9Ys3LeB&BB@n~IOMsm2FdOkO07#ZVYaS*7 z<0K#?3jo>hG~eJM6IfA|MO_oek!-EZIpPmCMsk0t9_(0mJAVFYoR??QKqPC>U{ z5Gia167?(a6IQGFMNQto;i*Mu^kuv}B+G+1iLNF8q3pFu_cH00{7IU}z$4Aiw?J>n zfVD2byp`^b_AdSp;jKyp9gC>ba|=6&=mRxvDcb={Wq??MfR&x)nZ~`ivKp^q3SaX z6v|s@H-Mg#sbMntMeM<@X&x-(BHG+&2hq}Fz$X%z#KRfjbOeE2iXNk7rE1blx_-MO zL|+9iOW?w@fiDRB49HQJc0cfdRxbTF#-ZIki~}9DS++G!E-KF*qYD>u`J49Eq2i;i;>_mdhyaHbRb#NZ1zCWq!rO4QKv;~pvlgK zrX=v31elX73j-=kE2bfKS=?NPHhY)VR5=Nk<{cE5#YOZe;qsHir7@&(zCdwN0pYUP z;nJSyww7&T&8n~+cH+ya6J@)$DxWBZb-m^c3gQzjW_{JDjcP6P_T)!yGjJ__vp{dn zz^Z;VEi~AbF7$YwQ+J#T<%j z-QtEqch$H-6rSq5ADscCYt~ro4)9jWpg8Q2E&f>SryPaj$cZpoZ{yDkKHkDh|7)P| zvgI{oGi3N1NMucf>?ADdBhp!ebcXDc3IbzEQi*mpP2tG`#CB}*lB)1Dd?g>RUg>A+ zG~N>42m*<>qeM5Uhv<9j9*&AUeUn}i%7&%UaUv{li?0#ABZAWrhFOYpH=U{}Y*E2h zGUZjf=pDp$!Z%#D7FdE|@6-hBCnV6=>@hmgUzefG%VZU}6I1u){s5aj+(|+aGG)Y< zU(KgdYqVp@FDQQ}tl#nqKsi;+Goss4ezl)qq)>HAUsj+Mt)UCA6wmWuaGX|mQg zHCL<8NX3gg_SFM;?5cdYdfB5bEQR2k0F{qHw_Y34Nz(@VN9;rOGHw&2Zo18I59HGF zh{GY8rf&L&^-^jg`yi&AfOGi`E$pv8zG)Pmx-Jb83UjjQZswWp-!Hi@7cj63nlarx zW4a^Uvze7=j5Z&d?l|w#N8I!hQ58F@?sv{OKwH)RDuSM;=_};AjEpZVOwFg5Z!JvB zQ_MXJ6G!t~sg~*%CjKerISZ5U6jP-rLou1LN#G=_ZxPjfifLzI>OIBuv@i^WPOA4T zOoOKwuZ3y&6q9aY8a>6V1JjD^SrT&??Xid$%^l1M3)A>1=8}bBq;Y6&neQ!iS51yx zfe_Uf3zsdcrg4vN(Dl;18^`1FdO$1>HR=Uj$NtGZOUt_goxpt<8+5%E6USV_7?|aQ z)CbOkz6;W{S9d6Lt;OEdjO8r1ESq3`;5_!ecxPtGW$w!TYdV=OZ)9bf=VUq`X0xnJ z^PEh#gUhlq&2ut61@1E|)6bvHG{wqvc||+ZsVdVwWJ-_H)K}Gw6$jN<)q|EXOs&8f zT8`o_zext|e+XI@Z`GcKDs4r8Rgh(LcB%b}B%lF(vfi4Y6{38C8wB)IUsVrUDYzws zO<*x}6?X@%65KVy>v3XNWDXpy1+B&etdOg?)#to}_;Qy;fhV@&m`{OLo6ae3jjsrl z{-$nm^X1FxssH_D_1alhhgwn3+_KzV(OdfvDd=D;Iv1vrzAOANn$q=F3@MD!W5we8 zrmE;0cEYs6XgeXtR;_}pb7Gtis>g;(8V4&EwwY~KxC6akm7y-R)o0ZA{v6cUQ=pa3 zpbYAQ^vvAeRGx1j=_~K(@Aj7O+6Og`fzLs{tDd3-HFYF<5jo^X_SJ%t9Lf9+<`%aO z1||7hvRa7&Us*M>KNuV?k?%@|ua(|`d{{jw3A3Y6`$1(#z6pl0Tw!3e@(v$X4@#1P zl5AB~phD!^U~tV8<~=ZPMh*Z|$;NC3vo^A8oEFr~F43y*KwXdQ8V|~2Q$55XSaq)^ z=WQ1UwZ7>Hy^Ri&6lV2T>9afL*Ru|BA-6l1rLNHI15>l~G9&f(xu!a5WBKF z$Xk~qn-=v^2-t10itr@?+&kfqN|bFW9;tpLD3w|4>P13?q*vpV>>l|EYLV1QO6RUj1@VEG$;PWv7t7mC1p1l_C=@$b8NzG$~Lf z{imSV8V+$4;v*bl>_8@3CIuR)_@ik0zk^aa4ZQlS5G{kCIgliQ`8>SDz>wKlLemNL z1Nx}s)k&^iWYGT!n#6mGnbU-Z2~Z}MGjpg@<=avNqeM`24M=1LE>+H)45Uhc#?PDw z%q^@k4kHO7f})X8CPy`lHC{=v8d&M1*g{}aVU=%-RE_wc=<*KxkBDcV&SIZO;4`cs z!B(ow2*_v_G)YmvOC&urlTA2dO&=b925@tYHQji40Q`r!mvp^fDb5+`Ul@YAJm*Gh zTCe><4e&%Xj@cQRzXj0s8;EtkCf6@6cp*sE7*yOEkC~pyt%KFgZSt>`c~bt3G=I!= z*XC2VCD-up3$xW4%y}(juB(x2omovruE3A&L1ayUrmb;NMLRdBiA^VZw%UFoyo;+tXN~ZdIyS6w{9HeUUvxcIRJBPW1Tmf zr(FO$rN+ATJe&qTry@SRB?ZD<^9%5u1oAbF>%X-sq{>V_9?@Qb-GX&kItQAD~Dku6Ci z!t!F}HWzh9T&tsLNA?9S@USVd-W4FWKX5^WJey6|xmz{#^-tl0a)BB20Ca+$_Ubs% zq`i`l(xj$xC*PR`feo=AC39%_o49O7Rx$JNI?kKB)CdyzydmFsB(n0uyBbfo2`F>xwdf1SU$c z1XB;Qvo0p`ZBR0T2WN@LuTv7%Lo$K*e=n!J)m~W2ok71z9>|=z{fRZ z010f)0FqT!29OE4C}dV8s$BpxE@KVs&j7L^3$4qlO@dRB|L07m_AF8~1~i`r*25-j zUz?u}{ z!-)>pc967jxV}Q*Wz4CQK!%*)z?yWU4|g~T`atuBlVA{mcbzhm18u2S46Nx@<&+wZ z21Yv6QwU753Vm1=8r9RQ2ac(r85_l&hx+|X}u`{8g;!0X{{LhEWTV@ zDo?7c*9{!Y0J8D4&fjCuo_j~IK-*JL1^|nWx~9#KUP@fJT=O8 z4y;C%H)c8sSj}Ajm6L!`dHt2bD(9&x_X@05SmU3G_sVLAYSjCL{;fuktPfP7rOE>W z$I_}s7RGY@KLF=9jjSw9+u#TIN?;Wa^f39pu5>--)mdfjZqERcRg6L6;N-wMGDz$d z`bG>Ofpx6TMVxtRquZl*GcRv+mv1m!;%cB>bUflFY!9qbnC=6Kt764%)jRfmR|^bP zghtk3vsRY77T@0PndPqM4f4g2etLNvsP2g&!(9uX~j|j zucFw}%{N2>l_J#J5L(Ga%=*=@<`dAfb_A>XRaKZqeU=;)y!`L+PMp~ z=TbOsHc?d}bSfsgFAghaRs$iS1&T4oTYFGb@jS{l5#!m zmha8vE$*Nn7vC*$`E;3(OuQtDx#3URWx?gR z^Q9#XxHzDi;I7AAba0GyA9lx|l27<_J}m>*#9T8AE-jUuPh`g3W)){!eoD-#*V zs+O@GV{YsSCE*f4<`O$f@URA0Edj*(;Z|U?m(cddJnaMaNT7g+Pk>w;mwT*=TL+7u zT_sGSxeK(r_(9uKNx(TS`;9^E?qh4oar+pByKFBxPRx}A_XcQ2i#a4vm&uKil`bR2 zTz|-2qUODs8lRgYO4drBesb#NRJJ3ud$dhcPM~S`_G`xF8)onAeE7N+H9+1cs*vcK zIeWW1$$xdgkfD4!tr|kvYF^mRb==p?zqY$$OVE}yU-NNsqm+`NJ=y)traRoF^sQ#M z9bDAi(A>6z?{Ng0`8(XztL{=N6|GNoR4RL(QmJU2TaRp&%3fq^*?*WXX1iPK z!_6hx?vZtF<+7Y7D@jI9Uv;B?^zU^N`lDITwf}^i-P;A$62E1)w6CDnOO5ivTwSii+-`$q> zn;Ax`YJ4wT(Th7jYpw{-OUhXL1-h*-*&l6x$ovEo$>5;mv6nYK)Wql-11ASME^~N zcK@4FF8hQM(kNNaOMsTiY7exPz-Au00bMEo?F=c3Ie0g&HKp46BJz+ed^v_M%FK7C z#zDn0m0Tr|qfXBFK5+WLO{wc`6kXPwa@tE(pYG1M{Cw1haqhI$j6L0jnYBMVf`X#E zEr&;c>F93G_W?uun?5=227JM}agIA)&o_tWxJ&wX7yXZ3!!51oe|ir3|Gl{<#~p)- z&`__}Ka3uO+acfLSb%1+^p2YD{Rx%a^KmrS(W)i7tp3!sH@M{R=rO3Hs44HLYgux= zf9(#@W9xH;*j>V#fHjpV+^5mb$Go$?0r!R3c@L{@4|)7U4OP)|?4fE?Al9%rJfAU% z4`k#@ra<}9Ec{|@qZcr8?H!Gp%6PMhf^DiCAp?UVaM@(fj5j31S67K0-@#koLD=KX zrkb#+0efkL1+>tfR%DV+BgmwWt-WJ%Kvi3?Ut)wL7uSOM#NHjMeShtv(y_9zYCgzQ z(s;fLotAwdbG(I7&umFFYB65gkn&59P0dTVDPz{Ycz^kTLBegvuyFX~o2U1>pT|D=?PHAkHOCpF4op#FRKn&`u%>M`!G2R++QxTFf_z;nB$Mzk z;BDGw!R3Ulj7JCXh&QbPOu=1G*zVxI1b5=U8pP`lNx5?gdy89R)TSRH`N0~a5)Rtq z(I!TSO?TB8mGF^+`wQGdYm7=b${4l(eXDBQ3dg7eGYhm^;sa=bZF~R& zu^C&w*+&8gc&G@-G~mEC9-@G%GDaPs0-F;6-qtb>EassJkci-KA^88_81?H#Ym7Qz zs1{3-R{XWV>YoSHE9t)3b}M@25W9A3fMre&yr>eeX3UdK;+NW*wE0EkDPP0!-4ZWb z@uEqaM+5IEO^RPmxej9;Hg5!0Dou)C>EP)3o4@>TO$t?-6u;5dq|LV=`Nh(t_)WIf zZ2k|(`%07Ic~@U`mMuECe=JRk&!!@WUbU*m&Vudj93DU`QgbxvK>S)X>Ch>Z;_wDd z+k%1H;wOQnJd_4XN?!BJv8Z8c8vdjn)Hjk!+Jgm#S$qq3q)!Lf9#HpJ17FJT~b3B8^RK%8jbTvcy-$y!{ z!#{S9(%&_I`Pf~t>PO0F^7s^ki*mz{WGJ7>4;d4ZmsjIKqAI7c$A> zu?#{-Us0dv^0y_L(MeflZxtWfOe775_0>*QFXS_N-uBjXcyVMRuPO=&gJ(V zWZ1cwDvX~ZIcM-JPlVJE!w~IpeIyEV^tAfs7okw|uUz*K{WWvoQFm`+Po8<|sJncN zUh*yb*+)FCml=-FB6uJ5rP@3OHx`mXvkW0^u@z@&Yu&P%w*9cj^$P2Ikr?)^Zv(x} z8lSksycp=MX~duD=yCN#Nwv2?oB=+=^(e7UGxA0fTW6lf^+pLSQWrk+TZGt$AnTv^ zxO#g?g0^kL$Zz9A(ORi89#=o{Uf{EhGH&Y>mOKFE80g@(fLj+fqCdF7{u;JN2=jqc zVIu~E9O4lE3GNT`mrr=XZz1KUY}++5h{*G}7D=v4KwurVM@vB;YR2)?VIxvtFeS*! zeS0S&JE(6kdR%G2Rynu73bK0z&A52IK>J2sLzG{X84`NmF|DHHA-#nmL?jhR~#=B$}xi&FjKwfjwXgj_E;NCd|ionK8%RrM%~} zDA_2rNdFDKU(P|vGX?92s85cf)+?CLX(hh&xKgWV#))@C_~`ZyrUg5eNMNR>Wka#! zPlR9MOA*>yFc{HnCFLDFbO3m5$2fV2hu#3+dp1sGY@jW3OM!e0ttHCp~mtI~`pb;04A%l$QFB^2nz%t%=5wo=lhB<_gc)+L>2SAirFkN_!p9J{4htxdNlgD8Bo!KzYT{oOb8-|8)12~PA_Qa3%VPy& zp}9AYZ-@Guzvj8CJ*UWix5$i3RTNp|Nk#U;Nxn$#Z@zibUERB|KpV)UTbmf}6@Z+6{$RVu$cxdmZ*iHjKLHM6Hop$?7@wE1DSrle1KTMRJoVXX!&?WJ zYF|1TaF|g2l8?vurZO3AVtT!g&f?ukt#?uj*MVk=gee~6{OL*l&>VP)=u$ulCD(x& znp$@HT)pZltpwjkhv`Tv(&M;CpQ>Ch`4bI#}P;(DT) z@wvOXe+b>pS`_{w-Tdiu7G`!*^_^9z>Q@6jMz$}}7|or}0|{C?TD7;w$UtvpT0OQ8 z|5u0jcu1IkwQ9EA)DN@Wxu%x&X1=#sM$o8*+Y${AZAd;OcMkeESd5jgM z%qd^E%k>*ON2&l{Ig4#gG1O)ybyY$cOV?8I?Id0ujZyLHZF+6&DLeLU6&s3ltk^^m z&)6H9ae9^c&lm1UPqQGE?F$^4WDOzql+u;%r`)dv#JOdD;W4^LDwpxdDNeDEx2S7| z3pMfkig}DUr3}F~b)2GB-vV8=$fN&d3u;Vf8aGG;b-HB@$x;6+On6Z9b6Mkf&5q4pniR!{2P6i^C-JYqBgsN&EzlLF|~Hp;W-{n7{hXq2;wv^T>Y0Itg1$- zsvh~$U8UMmRe1f5lyGZM|M!yNeFv+1dZ|?t{Wnz;v-oKa)b7<}^RH zRUq-N^z&?FDFjrTC zYs!8i)1&{0utHmETJgS`@zosC)4F=T-CnyP+Vzigt)}@hL)UmzXkFQBqA-sew*sR zyJvx%2~t|5yzTMsUIKCvNNJD?B6}~M?Cy2IYQfSX73~(;eGsHH)9{%dy^`G`yDxz} z2huF*(JM3k5Z`(?%WJ!T1&Jf9huV}mMDG5FJ>F4NkU!(=yHH4zm9?8Jrx|a|P=%ItkYA3tM3W zVieYR_giN8S$9NPUz!pj{}L%O4p9B#7v4BIz0`c;th5DNCQpuXqE!i^K0)j3Dz*tid#Nt?^|xguOE z$Wc~eMiPbN4n)TejJ#;$J{O#wiC7IO%r(K;nH1Q#e+v$6kitB+aOap0IC+fnwQ=ps z2{J_y^1)NZ@EoUgI9O9uX%cFq+F1_f$SV1%H#}*onQ@QK$xY<6ZdwItUGr8x-&`I0 zn1)e1)EC=YZQqrH)YA5QHUg_@0-Cv-)68FhI|+_vmJM9h%vZtXgQJ;yIL-V7+#|u6 z9@+7hrr%o@5VfHX_qUsVueTM*rkg2YN@GuIfi~_`Ez4Iba!vMoyn~?6pXM2F=d~AG zu(uaoe%w$p@1+?RnyCeK&j1f$tp472fdTdk`nav)VDZZWo@W@n^ZXflxvZe?MLK&& z;3Rrw2^|XjD@FIr5;|d)&~0qtj;$58SJ3yS!e%1urQODjv6jcr|0vx|W~+k4-y(u-}qRN*;^vXppSCgdpb1vOMdNY@>gha)#%XnZ*?W%+P}xcEiR z6cF24)v>dxV;?3*7sgOWxoK?gY#>b-bOj+Z7(HGklll1jL6Yo!dCgcW;!wabOyE>ik9egoWb?k*)HhSSViIA7>v~ki$ zp}puQC>b~FIK;H2^yrI~h!J6ogWB#K7g@h~mrtcgRaDV`sHo1E*!R8xD%*4hPRVuZz{7}B{+U)7d1%1&s z%@d*eBBi)=$o%c1yS+ZfOuXbSU*Q77cCU+djc+YQ$8xr-Uf0N@(_Z?^9C68Atx{R> z287T+j!Av5{}P4szRO<^3%3@( z_dcY;zttF_fvU^I^al1M0w@n%>E0k^C~OLX0cKw-ke`|$>EmK3_MsR1hL~3`yPHHT z<#9%JHpDjj>$UtOCs+C;9mI}9AgY6SXg0p$u2SwggjcW}yY~3&^=zxL?+=i_nd7gx zL+kE;PSf@WV-c=|l1m58xH=8$VW0WyP4Q0UOu_z2Kn1k#3iDFgP`^6Z{Q8PJHgtfq zHejs^V39TWT+QS^lj(2c%&@C`apmd*!mLtWJq}-wzrQ{(7zCzfe{Gi4Xpg_A!Jz3X^_t$F#k~kCoKc0j48h~p|jUPYf0cU1^ zF48F(FFAf^q}L?F%g8`wsJ?5b98_!Cqw>2vNBghrDqtXgtbpXsXjG10O>5wCr{}GU*;Xh)y_e+f*NOmo)?5hc!a_d0rn<6NH{hWfnG+S8O)@w$w5?!SIAsD(PZ<krEisO{^7 zZxl1rue;0WubZ2%yE}U?uE2V<6_fDYy=t14`adUCb^+PEvgLmhzd_qOuzzwt z%^Z7!uRh%)<}mP)?IL_$T)7BSv-BY&Q?N(@^xjJuBx*@9=M^6`axlnZ;uu`RRX4u7 zZ%0RheU9+N1e#0sHom7vA3(Ved?o>0odb`6``Q79$V>UQ+K%$Vh*r6HpL5G-;CI_Z zkfM5p3kSqrV$9rjjo70Si@_6I9cv}#I~gI3|3}xGz~^*)58U@YH_1(&Cs9j~SQA?X ziGEv?$BYM0io_Fe6J5X8Roe$U+dgx~vr z-{b|0$3kh33rR7I3&E3|wSv_(+5T7=mTs*RtaeC?zdO*8ussce z<-D;d=+#W!R)3*vr{M3L=2*Riu!RhEOb=ZbTH5V5SNJCxHUoc`<2~f`;Gau+*)M+) ziWy`tdb6~hy+t2|HVpm+b#%VGXoK%begt4EvFqX_9Or;DsH4z^q91;7r51oSI|ta^ z{d{3zjnaRwsH2@}KHk7cwK?`8O}w)TQ%dl(51vaao}VZMNDZ$8*Q9{PN;0UiFQ0LN_n_o!$)o{ZSRGeh77>)pz1T>X$T*gt5SjijIimF^X@5kEWq zoX~8u(e^uL;~Il^hB9=k{8%}nL@vndEs={Ra#B^>>8OnhV*IF$FTS3?lMJt`9Can? zQ)MLXd??1J_%RwquKq=3FUV=#I3JMzjofPqE3KquQhSk>F%e=!=sjhhVgLNW9}6ut zA>o|7*bM$y$&yv=LO@idz6d*3r&>`~^NAJrf?KNONan@-Dt$}!UKJsY-W}YsfdpGK zE(8<{Yo=oBDTBq1t@k#u&8!2&276Vk{Z7`>F9sA2TdqP%tJ3ZCrMbuK;&Q9fMX=pa zv0=r9ayvGx2C+A+z83=`^)Ic-B+`#te_jmu*qebQYY(Nu8ST{@{g`xT&|VV;>w{5O zfxpG1m$FsZ8o02gUQu2lyJG$clRWrG0z(L!xh!UrFKjVk3moMWP*C+@h7k;(nB7jd zTCTCs zD}F_ZiDLZo-lEL2pSMv?@en#j);MyCJ!-oL&Q*$hVYMJXU=&_SW8m$4bB zMS1U^fhALcz%4#4H(jLp%3#e^ok>PURa-=fR0|PO&{Pc2GuFP#0Wl$4XjmBjdn75_ zTfrFtt-P}jGGqixQ)cFgK{{jFtCdyAY!1|g{eSF_>Eg7|+El{E^%~eii|LN~tlJqS zK#D!rVtPAmv6ePmyTERrKE5zmy>@d7jj%N>X1>ftzgrjcW0sc2K;qSM9MoJ_`hbOo z>mt&}AhoV0P?af|Q6W#3Hm9|)lP6QDm_BI0E2m*+g?glE1GExPbsRl`F5)SV<4a(q zcrc&WO#vpti#fe%29CfxBT6iH7kqppAh4}`Iw8BXm8!IrlvXHaFs9hmX1>yX2F*NQ zX@B8a>MIQiT*utkF6}IylTzAVDb3n^g#%JlBP~qiFvppgkyPR8LLbumq?Y}FQib(- zfm{yK^`-G}_DmsNUl~tDkfv2}svsvuCwG<(3fHc2G)hg3^Ku9G^=#a(Ko$l#_Ebz>38zif49|}_gra$p z(wcCM4U0cp%dQ1f4>?Xx-+eQB6YBKfaqIE5fQEWO>+Q^d>NyKKDyimgN+r#+QZoZ$ z^iEcKW8?s3}=Dp~5oF!ZNI{k*>vVS`(BTJj5-t*^J75yQCb0ZUF zb_=bx0mZjrigR9)BE2{RY{wo%IOEMkdJ{{>CK8u?p3COKP_p*vUa65g?EU zNL=)r@=8tm>xN^%5l3(~T*h}^S z-2;idx!j>Rom!`(nH9z9#qe;;zoIz(O+3{diqjk5k=NovYkDi7d2xr2JSadnI`6#|g$Tf#za3k`ngwU59{yT@J{jJ#TMW6hQ z(EXj?q4@6@JmnmU|CZRwLNWF+D$zBeky_DI9JHpdm&_g&l~(9@C!nyN*Ye&8$d4&xiLZh6=N*uxSXb{r zN+~PiZa@kC>fQ}#mLnhHAM>8am0U#A545r7-3=(|oq+VZl)`v)vP@si<0>AhX%$e% ze|G>{m+8`@zXX;RVhET3e4*an5m;5z`Y^u!{V(u0DWZ3KTqQ8L~6)Z?lsfehRm!Ll)!z#bhuF5h{fjc*fJ zgTTH494diOB=mkCWulRvu00{vlljA9yy+ z2j;Ov_4q`VmO4hJgdv}MT)8nYAIucet`ffvSR?mJ$`ym2;zF0G|132fwJ@IFBn3q94kVyVP#8 z8wgZIZK0?RQZCV~y}BN6C5ndHoJVw2YJ{xm6ZNO)Ah*jOJ3>j=Yb#ZB9Zk&%AKtN= zJq&n*FaPodNU_ROM@a;EBysJwVtv4Pe$XY-F;V0~3n}4oRfFq~6kSp%O~1K_5Q%hb zrk`z&$0O3QxfAe_v?6z(DG}MsD6&~@suK~}{2ee2q2&In*iVoFFLw_70dc(T*2=%toig+9lU-yW;$HL>{@S=VI zS(gQs#4(_m#D@`i4*`~kPWD^HmD=g5X)9NH^ncK<6^0Z4PD|psMqQNB(?J^iTMglv}X6w#Z2E)d`P21nHC)1zS)|Tl%1uv<{S* z5{|(3{>`Jy?H5wy7A(Fkvu>d|a#NlrJ5`0xeVLpIE4Sj?a>=CBB|}z&ki~)Ks!3yv7cSGlAiG<#pJ%*VIq{>^P)WV?0 zcW=?wT7!PsmKmLxGs)Hkp%|>G`;z%Y5ulhK`6j3Dx`OATj%)$}L>^tqnVK3Y7nXnc(eBoa`|%jPmW126oasUFpgdhw42M)cCrduH}hGD7O^>3KUh@yQ}Vm zzakJv*z5#Pk{dg6B7S6Y+na=??ep|-1bSOv!aiY%IF}U0MBcT91oRiSQF+yPux%(Q zhRCvn2nEULPg)C4kKCl?ssYtX+s2aSONKcH`Np$v&H(dV3ne7q4uz$uplka=gRvTNR_2&@fV%K;mn_Wt$ZZ%aDrV zwi|@KuvyXAVa0Zo-S+%8E8cZjv7NDFdvTi;YMw56YXj9`g(?zOc#ScQm!Ua(HErf@&TKQ!&g4JS}sO zZ6Y|AwegWcuiL)L*C?S$(kSwzQQBe_+mvh}%_gD53!SvaHw`?oLNSdIDh()C!x#bi z?Z${LW;e!m6z29lc4NdM+R_+1pnm&vyD=&_jlm{gZAU)4F)BKZv7-cG#q7q2cjR(M zBJIDU8m*zqq&588*p0!CDJ7RXT9d1#uQ5m^jWNj+Ph1z=?b$!)zGd*>{<}bH${J{{g1B-aa zBs(EX`OS-{GISOhvYaEARW)` zDr8SBI|rCt%Bx{f2*2IA%kS4jeCLT6^_)(_^VZ7&iFcmHkbjgpzATR##g6!>PA zZvCiOZ2d@qX#e|#%q$6e+X>rF*q`*d;9PF~gf!k(R4u7VY* z`DVOGjXX6!w~LSC7=ddYVc7K^37d+CF0-p0@S$%a@C}m}3Vi`_jBtTBNF)=1T|EI= zkcy>UcMSptNDg;#O7Kn>feZ22S%PGZ++B+l4AZ+9{SEgUI2e9+@mh~$5aNCp-+Evz z%0uRh6Z~Y5pwaHK@9K~)#9NqBL`NFM# z8zu?xEZykV<)o`KuW{=?sQ%)}pgpfiX+gpYQ{DPvw~V{U9xtL**Vey%bzbAvXELY; zT(svk_{2gj*oI;uu;vx&vL(i?H}DY{T*}(#Hp6q+Ga0we@Fqk*belENAXPkOOpRX5 zUEF$-5`9W+-rBAAgTCNW&=71mG&qBq1j%C7&aG~JfX$wH9y8LL^9%%x7nKvdAFUDC z%&os!%+?9J&ePB*2^=(lKxf{v8_C-BoWLmhOATkw-?oVt_El8#yXnUq0jf2gIQpMB^#I2V{bIA;Z&+a+(i1p1tjF8;c zlwh;0-rd>|Y?f+Q)@coA1~N}(Af?N>uP_5KLN3#+Xu>PBp)&(3Qqfz_EQoQDgch^Fx)%&Yp%!c8d?E|v z82=Ku%~p%K!c_!bMO8KrID;9In6L{&NQ$js%G1xjDL5fQm+{Q?n>o}r@{85 z18eODtKu}+o`Zz#$97Uk=uNq1+03uvWBwTuobuU$IjK8od1~?QsLf zg-|FMpfLCQHihLK3ioy-L3@qYPRp2eQ|2tAotEG<@!pR~*-HYj8A9HQ zaurp?X-1v`R%C9>s{+>Bxy?E`+)8gmxk}pR)%4tEN$;4u6fJMcjr=XKucGuVk+yxc zm$7*7AZ1?>SQ_j#3HTNGLD^RXR<-S`z3_VPPEthgj?%ZHrNqA4`xLlOV6Tz{D*K9a zTr5rO%SRsxY!;>ei=xTnl@NyTebIyl=ZeyOdm{ERI_>L7;McKHx^GX!K1|nrg9&UE z7p3nNMncoRRA3^3n~Fy1^14I?&Li-G%0)kkHFk;FXQ9UDE)j0sic@?z(QGD58k60c`Mp^Zu;`< zBKBWp-bLv?yKMha0uPfS?oyO~SMb1-{TG3=1Pby8KIBa1_ur$FeO(TIbeG;Y(5v1> z!&ag7YnaAnSR-$WD%Tb0lzYIVX$OMra$Ra`97P<+2ZRc?B|dESTQfn15pTXs>de8GhB7SzbUn?zNB zO5*t&2g>n4EfznG{8!-^%kf73YXmg~-gS(>alsn;jweDFrO!g)<>O_iS|T~{Arq_v zZ5)k95;UGl_=?8EkH&LCvo)UWcQoDsl!(>olXH}R>wB@)Yu_3J? z`OGp6huM~6ezt!&TMX>|V;l(!%0~ThnXek4XVj)nu_g0&w{GNP zYPQ%4%x}g=ZWEL8=WS8C1tMiqhUKMGNDsCOD;3i&zgbXUYYoY7Cg_W-75PnB2S1tL z3@@{YIxpQAr4K?@9D6ZJA1?jA01YmOTOE5b!-@zq3)Dhyh~Za-5_8{-(jzHRS*GFp zC~?pLLdwVjFrFxcz1YiD?I@i$hDO*TYiJm@U;%4Qm|4tJtJ*1htqWmhgHRzwdT0q- zyJFac|82b$Zk8z(sqCvLJrvEQ?5ptSPTIWL_Emvg)@0JMJz`zBS-gl`YarCy_SJ|& zVqY!a6SZ9IlL8m4=i%5Vi>!PF%wmNKV9tC!H)`#xm9{OZ5y4iA0%q9|cc6?FQ9oif zQGxLV%DOId?R0m%}7n1KcL0X5B ziVj(NisHDSR^gGX9(NCpYDU*;o`hJ$$^cSmx-zb!nw7GRs{_9y?F*D~71hics}F1< z%r>r~nyV(me+)QMvnaFR-BB$>;Z7p}4R+u>N=e~I#}*nx0$Pw`U~2K>z;hBj)i_4c zoYl=gi&_zd%)H*_)5+dfs3|nGur#}Z<$aY|MA$;Rg33UVN^b==Q^6Ke zBo!SEL(?R#tuvo&ru#j}}b{I3)E7 zte})yi*lN(Y!Qyo`8H6rK+*lF_QG={+hLU^r_qY83)1gcWr~;{@5%X?%7Ma#=<{Vn zE`#)A5e^$}QP>qmuC8%G*~9mt{F-(s53k&Vbl=wULs;L3N;@6demyawKJA>hE=Z4& zf6#y+{aj(uHza%qsLPr}^!Y69)N-$^N#y+_NWaQ@S@bsyN70mes4egThFM z&qGMSp>B9$7(oxwK8JWkYS0kcAx45jV;KEnN@i)N*$QLCK+2(+z|%lBoFJ~hUyO>}P_mS%* z>I>CRM(|8>vR+KmWb3REsouKShS_Hd>BUj9a~AHntO1c`g!dwx%HAB%Y^BghhrS*G zr>4rJNS<1J^e&JU8we||$3b#4pb0yYuD^*@E&uNrspq5_ufIuz_FJOUnw2gCP2f50fruJ60aG!IqFK>B>GrA)@l zGo_g@88a9;)CSWGOxB8}{$I5h%1=}K4nleUK=XSjFNulny7QDGas#~Eb+N*s{r5Uh z9Ln=ccfK2lFM(eCUloV+%k)za@r3jyLi#?3^#9%^StB3mAK;PCMfxbe%<%e>azU0C zGzS$jo*--PfhO6?a<>9`y8@lXQ;PBIzyA383O12t_;8tC_Y#`~JpKQuHBxff5!aIM zXLCg7*uN52$;LRA-Lc+75M+8&{y(M0`Njas9J>is-bRFF0}7%111K2*3>D8c9J7I$ z=<8U+ChbhZOVTJv^gLof;?Kc%RPjG=kp3})YN=6zV*>eq1AY;n=LJ#u??5E~KimD) zC|`UT@#(-?^nCgCIFPmf_ENpGfl?T#;;qFc&9F)c!J@bEs?Rvsy5wxUtVR>oRX7kx z|56LwBh+OvXWL4C>sd*&fVVd2&Oob{zgUp|6^&8qn9$dWqOJqi#DnJe?;&tsh%F{W z_OWYy7XJGhgj&v|cf zSJ6)T&h3O31HvTu5a=cFECKa_x4D{ce_xO$h~&sqODOjfcwZj@-u(515f0% z+{)Jkxsr~bd^?jLH+Ii+A6x_U=H(l7#Rr2R0 z?`B}5crf>l?*sPW+4l^`3E)3rRC7TeX-kE^Kbqhxz-7uh-@#$OM2dLRW!14NG5XFA zRwcGCw;Ay%J(*4=2H){Q_j%k#b(~l4`b4Lm8*h(TrMWMxw3*L)ehe1k_hjPChiu1{ zYMr0{N|$NYlwwe=7f~JOVG>043eu-yX_R?EIlOz+PUHh}3i-WID3@NtR}m;Lo`N{w z-HE#5p`)Kb1D@zC9#p`I;Z)+pXX2}Y1Cmdqil+gNg}{9Aw8ViCaAE^S$fZtM+NC74 z#3i9sVm~G3C~z1OW-piSxi&@%)bCCU9Lmb3ev~D4cjG zKFU9d$euLBGZqKB;iPCyIgxXcnw$(5Pd^;xfD(>faY?Oh@Z={aa*Q&vD_ZP|lZ}7| z!qQ9hV$K9CjxzIm&)*gHl8iN1HQK%BCoOZSj^;Ykj^HG_CG1|TWh{|(gG))0M^g1g z3>Xv3jFVKaS`?RtIWa%IjIl@FOSG}dvq56mFeJ8sJ@j6h>O>uTnQ4$3b}sD|bt^?< zrPvOnT{$jm;!p$enQfX8Kh~EQRd`FdP#g2KiCE~ku@=g5>$VYt9Gz?fc;9!{giay# zB;u(B2kDDYY-Q)eU)eTuvKP>!IGzzI1?x~M-78B%_+f_=n!O}s`zcP|2cASY@j z`D$)-iW}Loyc&P3F$C#LX{)klC{jAh$uEEun(EYDXLiKdVQm5Y_>AmuVZMvIwVi+8|sid*c=5%q<_Q}d;B+E}xB zK-We1PvwMZIb|=aUu2NZ#W6H~dn+Z{^y)XK#h4S>_CG1cY_5N9U5GKO=>J)T%bN{a z#mf>-pRPe?5eT(b7n>6+E3jG6+2XP+ggb6n&{+ni*qp)ryY|z9&KJVCe=e(TZ@kx> zOIu0??cq(?!Q;GeAhFh3rr5EcSnJE1ZxlFAd!oQjUY7%)Gj<2b?pGF@jabVHi8Wsj zek~2Z+N{N^%fwhSy!~;#M^Q~%+c(H};q@s}oP1bZGyZ&@rM*TkwVzsI(ApBn@e1fT zg@!v-S~^lV4ixe!#^yd$qJGc4LJJ(Jqg!hp%XUEEFc&NLPFd0BGmFt8Vam>iFSJ8ZpG!7P^07OW9ciHfC2&1%HQw4p3UpeM9b zH|&x=POwWlomb6!}V zu18oc!O&VTOsi;?DDopR@aMIl)+JRTD7HQ)gX%f0E@W0@*)Ww>)S5~u@ngYA@(K|S zO^k7;+sDY+kDv-z6!K!?G^60@IqGVtpb9nY_eZC%6Ly-saHL{>nYO$ z#YoBf8T;5zu%5{>TD)9sAm`-hGnRXX>9L+wWKk$Jlb7^Y*8lm$N{7)gYmXVCd)+7m|0ML?sp+?lmN8ewoK65S|UkGY=pf_2uk=| zoOnLO@eWW=JV`hZ({r*pE^HtU$UE0vJgGRK?cA_JNa7|%1L_G%*vJv3du6xX8`CK+ zZXbz|;B%u1i93m7Dll0Fkgz|ZlUSx!w z#s~k-Wr`;CV>hrv5WEk0D!pJ~06BkJlJsQDoq^|tcs|8b2H@y{5ze&gLKC32KQsC8iMU1r zUTm`8e#_E22CAA4jnZRSFRSzuBhzndh_`@mHTEWiFQSHD6O}H={vh~AV6NB^;eTgo zox@;lU!oNi{}8nVSSXsk!(LH*L_HGqEq9c7eK%}%EXO#o8qNk2Wsd_L6P%!81o;is z;!6mQjtnd+VO2u?2WmsLBBJ^`PLe@G;??i~qK;C=XUY&@>5(ary0E(AT3T0K1EmZr zuc9J}et`rD`pyhKaTHPcJIQX}O)}Vy0dX9Lbmi3&W1D4C1-Uj^YB7}(ho>#R7r+zo^k8Y{61BM`(*Y0t;1WIjl2}L{ zMD$W`AW1xM;F1Kerf7KJ&86>vY2u;smzDy*i-*cz+6HW51=hntlHu8jkA8WH$*$o! zs%fxMyL4MT=kdJB(#GY=PCw{!LDl7`_sJhze2bV{a@JkW)U|xN8Lt}Bq$*6h^ z9KnG^O#xC>biOQJ_YjR`R`sDoqoyyX0W%eAm)L!wT5b^u`@S9osS#Z7>Sv31HMMFK zSWc36#o!jPuf|ngzI%bY#yVm0hQxmuyep!TQXm%(8P)n(*Q+vvVQ>pBE4lt(GOH|q zPcT%bKxHTyt_UMSpl~g)f+~6tVik*3SZ$}o79{2va7awC?qjl1_Z8bRyE_OO-6>ak zlDE1ch1*>T)hto-Ae^pJT`uBqQ9&tYuT(}NnZXG8)oW(cton+ilQOfi%T#^)Wtr^y z;LK5$mQ+X@&g~aeCMRw7#LM`7`5M510@o91gJ-}!@who^Lu^Jdq|qJ01ocTAKBl@E zvLyY4p*Eu)AhY2o4RO2=G!sv29NmGg;-S?tl7W6s*P(o>!4sx>^sOSJ2{P76-^pMo z${0q7^c|VCW{jrqa1@FR^%dE*Jo?U@no(no5XcCVF%6J4f+vhHG9S)ZAk=;!B_qih z^uG)gvFiz4I70%Ci06@}UC9CP`2(-(DI?4k%#kaRL_KAMxsm`>5l>MZ&4DK3DTjkm z;|ecdTu-aw7zunn1kd{t(hlEZV6J#jBUk9iS1vfcC25qRevn6>NN-8F3&ApKTzP_2 zUU?+?K|a@%G*6}CK-DDnz`dCOSLAz5S4}`R5dBUauZ9A-Y3U?(UG@H6Nr*ar7l@ep z@T`>SoIBN{r^56%W!+BUi6m=LA0Dc6wTzn?WYSt6q7)@Efr-47qz?3C_bjr{kqV6` zb#OVao zQMyQtcG+Eo6#^NG0^16<9t@@`{&V8JuFqAi?F8^p5+pbYW(fj&-e$*B0IcuP2m+mZ zmHxM5OI@B!&C+@bU(iW4^HG;4-%^h$z&8^6gAw5AdQOkfug1aaCnVOa2b6gk-=`O|!@8JH~=qCe6d1E41y7QIvR zOR3wdYNzKmyVf=5Ic&2^*EDkk^yK9h1SQloi|LoG_BGA2aX|=V_l*&4gP^LWhEiBG zC87yanZ$@3owbN2SUB9|Rg7zqn?<}O!L_Y5HO=Ds2fm zW35PJ&N;?%*D_;WweDL9wagZL>@BsH8K#%Eeyv5C#?~J3jkPjru^(iyRq`$Kb#D$4 zml85bs4bJ3)};bpLpQVAM7w$hIKhI}@<&YE{A4wOCkzafakI84n?LF1S8oGZ;z0pi zqr;#DnX9~p^r$Zl_~*Knl2V`Pt)^`}6uE?o)P6H~kQTX&aZKxG{ri?#M3kqNCN$J} zKc-WcZd&A6)QU_>uDu3$Vmb{>CJe2r!b%YqQHQg_eaVog1h_0A85MS|Hc$(amvqR| zswc^|A)fpq`Rs*JdoNTj=|_@xfkwPcGnSMf#rV=1b*oO_Bq8@h8MRsZGnTwTqBcM) zk*Dgkcik=4q}r^HN*;BTg{Nd0$C^={>b@*p$kIt=p|{l)(GOs2>WdczWElZvk0R_f zHnNW&IlEdAsU;3K1f8Nd`WqNtz6c>>~xyB-bVb-tjS=JNDHg`;>PA zmlF6}i5^;HKU9T2wYgMD~|3L5Ci_c7a}5Elp|y#aBf>2S~5g)%J#J7ld9S zp4$=9g{xyENLXF9BUB}J1w)u{gnn6fmbIh~8dz*OBj z?f?Q)ueP=JZ8MUK7f#9FxM}swk_F}=Kl4V8TTqzM=o{%KV`IksAc@ymHS3vS1rFQs zGqU5G>=-v$;%{30h_4uE$B)d8Pku3O81eNZ?TBI75u-1S>n;)1thIu;Io&p4`#9BY zr%A!1kB)00@x84Z691*0zfpGnJSE4~mH63KM1A_-2CHg)GlI)GKd5gOeWU&(J9%Vw z@`hu^^-)W`prmI38dl*!rNAqq!oO>&=a#75R+D$k0`XUz6hWB9HaFwywtP;41};9t z*F$UhJVIfmwsZ2fye|<2ty%9-+ju)(Zl0l3;tQT{d0yhtHWJ@W)uN>weYFvnsMB(b z#Ct8h0nISoj$e<|+wmX%(Q<{vFSR39WJk;!(~^yDMuUS^qXuTFBDd7n!CDTaP83{^ zcjKd5Iv1ZA4RctN8kmJxiTk~Q8LPLoPEk~Wfi}j**`$1Up{1N|Z1{(jv!VH>zSDZQ zA;~V<$;xIYTl{rPw(S~?JXT6WBstFdxgi_rj#|4KnhB-Gcsljhis^D{*uT8SEAmF) zqX&oFD5hV@i5S;ZLEEhIjm$cQ?kFx7(^p~N+U-!FUNLHHGW|AZOp#4+~EJSLovM&*w{&gU^KW|OgF*uZY66GDfN%6 z{f(h;g!PE@+&5I{U9|sEtJb^pJhzoBf3en2?^2idt$!tMsFfwYG%H^de4DN6P4K;8 zb!=kB=LtpJgXR{i7_2%a6>M6{n%~4MSFm}GK^=Q*#lr0f$jG-n(M1awO&41?o0#o& zWUHxJL^rKZnws%?HS5QwoHaqxLtgzT$IRieGguHM?(@SYi8DB07wPj40z3nCuq zVmeXFiix^g5>BvQYi<@Vuryeucw#4@E2PXzye;vEtwzMVnm@7nHHW_u)-?I6Yps#L zp4J)u3dj+S&!ZC4A{2HqH*J{sHJMv1vx>AZWAr0d;}&e7jI+LJfr{;89cuwk$6Gnx zN6Z&kW#2a|idpeKt^b-e@_m?D+4@cXdRqr1?oZ2XX*S{$*DYGo2>#aSmS$nSy7f~_ zx<@-_?l0MOT*ouY4VbC~ChU+$R z2DSf~k`-X08f2a`$UI~nuFak-jnj-KyG0w8tjLS4HexWhd8lcbbd=0c@nqtlo|zHi zIfEkxD3?dFL}ABmLjF)0~ zHDVW217u?Pt76{4Q_oin^Q+9}QcS!5Er$B2V*GNYWOcCdFZqSy*DCxyN>;<_X|tvx zihM~rxRxvj4}*PXC&AT&$R#`R^#}T@0dmL6S6-PmVZ1M^X}i0W%*V7*n?^VJps>}a zjrorLwzaAagW!Jad>a~Szm@+3I?bO}r4P&m*9R}G0UywcuUj+audTJ|1G7SGj(MO^ zF91F~(}Odx`vdfGSYo_Z=vi9Rz6ixzTnd2{CRdqWtJH^POu5-Or!lnhrC)u5Eu&gy zI)N*-1kMx{4A4LGSGkO~ZY$+OL?PPx`9n@!^(M56GAD@E(qi1H99qC9;EZPt`C-5*c@@ykum-n9{}s08 zv^B#kR)&gsBLnu@N*9Uixi(;{Bx`C%Y|V~X{d2&2iRfWnBV$47l4ShLj;DZ@GXj>V z0%lo7+cA>wwyLx<f@i6+7~$TS@X)$(kj9y{!H6x7IQ{nN8(Ohn>h*(@N=Ne!yK<$2$?X zz{=U#tmx|e(5l-RV(M8_exzuMNXUC21tIxT-UtkACJ zK)ty2bys|7VEOySy4#iPC#>4t@a3`w%ir79@ACJrb*`H^5^dM5yP3uyV!&1o-3sewl3AUdNPaZW9{l`z8AR{WuQHjKGK^xJa=Bk>)}XR z?lfffYaqo+>}5v!uf(~;>ekDQ`0y~!gIL&mkJ{74-g1vKU&qQR_IRwUB&%yU_g>(R z;Aqeiq@u+t-&aNBj$s1pz8c7E^m`V}dv~(V_A)~ghB;|IeKpNg&ENuQqxTE~b>6b7 zH=d1V%A`IuR;>DcX>u*L71!I$Ulf&PERj0(`IW(uI(>;PrmA!W;!j)Mdz(cH$R}EE z}~6Jg^k z#&7e5Z6a(n+nnXpoW^YG;~z-Se@wh=ofRE&gQ@3@t5CgXl2q1LQ8_RpH+E^Prg%Tz z9QiR8TDVcF|hQv-`%6&8Zom$nh2H0P(clO&OQ0(>NHH zZpye6Dz85*rnfh%1C<-#xetaqvf30n#y>8E4AarWH<@1EWJ{dE=Rv*N%{KU2Q3$)= z{EKC2yLmmOo|;xaUbc39!eMKdKPvcUSD>?anAzP-2Kuq=;r|Yf;lQxLnk$HRJs?2! z@#L4=zn&X`*v)A^lXu6T{3`8Ae^u?;WRvDsl*qxFF*BJ+S!$6t+)d6%OYdgfG!I&B zKH(H5!`z+>RShftHnZ726`+1eI)zRE#)}65w>`oLF2GWw-OcX_ouL+cb}`$Q)u2AI zO#FZ4co(~kM2`D(^R*IW?^ z3#2kRuSjLIeeT0Kz|mSm1oIV%ZzF>(FT(rWl%5^mri8C&zw`MU;*)FPBOw*gz| z6)Ecp%;5xzQ0;^3|HcX$sTl36(S|>E^mipHO0x8C7->BzQqu6wc1ekm+>MPc(z56~ zH_x#!IlOb0KB0=VRqlcQu2x9xFph~D!v~TE^^!G@rP9UGS-KUb%$BA8u6WWU%U0>d zZ)(~t1JG&1+*{c)wYa{KdRs%t7DI{L4tXm?JRNXgK-`j*+{L}UxMF~E;-e?tqLR1X z6wfFeErEC0Xu5a~WhSaJ&&6#Frby0{xfat~ZjAsw7Z083))XLBJaFXJJm3fMY{l^x z@P~NT;@AW5F2}Wa30>&cS$xNY=yOu+15LZ_24s8l62nM6qlu#`n@(77T~e21cPXAz zwyq^_lZ^g;J5*xc)3iK+n$dr~#55-~5-94IrB7!ib9q%_COHiwVdz*DHqYw>x(RH} zGM6Y_=|)SIBbllh01Q~55+JGAn z^yz*asFNq_YH@S0_mf2d$bP5WRe{RIHFY<+q!6Mc-OCM6;t$9U^=)`^Qqe_a!_)0L z0A_-ir26zQsg#iLsUV`o*kHz979)8qfrk5P2NlSra2M##$!gv9bK0M*)!|xTjpTm?1+V^9>e8T4+9g>dC&45Lbd^{gkYSnv!NRFo|4p*yHvr z;Ct~T;aC9tA|7Gu3SfBwkTil9tEC2ve6C##mjmSbQC|&GQK^SU2&;ItWz)TLBxz|x zsH@@clGmn#c&G2$`7IbX0vm+x#>nE%xgwMlxDy3&`vh=UJZo?~1lW1%+KHC9!!UNo zBc4MzSZ=r@%iKH9;dldx7ECu?sb1OCxbrC?j1PBOvE^}BP?kP9kvgg-nrKFjOs%gKO&(;!j4igpKCyz{$ww&0+T-Qhi$PDbH^F;J~z4^&M(nZ-@} zt7wD3Q(Fp*BPGJbsWW5Xx(EU)AQF=kB(Jb&5ONQ))S1%iBsnR8s^x_dV$39{_;h=W z7^oEB%e$DJ*fXGe1XDxFZGVIhdi=7g>TDm8ukaaz)TV!X6_RbKQsPtf6+UCoTZ#-t z#bkzvKe~~+lXYq!Zzk(IXl0Dit4N=9s>zVF_jER;_Upf7D#^PUzB*3*WL6?iP2cJ7 zilMy+%ZdDT|G+yG)}77L-tB3Ib*#7EIRNYv&oLZmx;yL}b*;OI`nq!y-*sAZ-7QBh z);-g-yU5X<=cHNBz;QP}kViauag+z7_1ClO_X}Md(MIZ*%28DmwuIC*Qb(zfW@_J{ zYkfO1q2TXUrM=eo$kL~_W_^1_=Pa*2B~5e;Pu`_e=!0JPl-oOv6~jo}8fYpWrmT0r z0mjlZrtsqQuWjw2z*Zz@YP?SsiiFXQQ=ZylI9RPSqnrqvim-`j4lXBcG+2q|RNB@S zIcX>oO`d0q++g0)I3lNBY$c&=Bzh%pCPkMG?rX=Z!q(anF_?{GJ^cr0fog5ul`OBo z^(ot&$lr+!9H0f=EfMszq|)@!D*m7oFB>oQj)?}|m9hO&3YwTcdZ7ooX(K;%i3)~f6H?g4C)b**}&X}m|)?!LgY+8<9* zpfED9I+xw_s|yqG2Ebkn*Xk%7ZGjfzA=G;h0lt?azM41?)q8BKaIJ2rY4?i(5w!E_ z9+bQcdStWq>Yju$_}nimF*Hz~O`7r9Zi%5`?jtGpKcSshf0dR!X4zYM8K2ddzC?7u^d(d?xZ-5S^`X2$R>FM$Cu;E{xu-98gjt8% zQY(xfk=vo+f)TluutH8)w6Z4XdJ>2_R0(TILKrP!JDo6fa)mG{-~wSno=!rWI#Nz$ zfD&ap^lQm&53@s*u1yp~Q^!jtqGwAUn~JH^nGmVKm6FGP0c@8$RcMjq>}*yaDWu$~ zC!BI!fv8uzVEH`O^2^Y9Msx~KO|Rnbs*n?=EPnt)co%hlvB>dR1oTgrEuep51>fHQ ztdWfZe=^eDXF$5Y7m@n26pmBC3Gr0GkqKmor-r6IKu6wx&Q$HYSy}qDVAXYQlwFJp z8@3p&_1!-t$qR5^(D-fz4g~sv%-g<0H9f#+eNc{iOrxh|@8$J6(`#2?6`SE%HXDcS ze#u68ohu52ycYJ^pRIh{N>b*YbY}hR*Vy3~6h)8yUG*s9^Sm70IEyNLPz$Ks8`EeK z0Sg#T^b2S4%@k4c^2fD#{;qe(GMp7k{lYDh_XcFUYbf@Be&H!Tn(VNhK7Y+fJ4JIaA%Gn8oT2GTfg<&ihy@q~3%r6VZY;OJ8m?a|I?+GemFVTAQWA zBeGd)48-13Wg)XVr_91{ z?xcgMWt++SQPBT2Qb7n^SXF z|H(oU6I=E))F#(-k{}zLE;hVgK{Jb}a3C+7*0NA!JKrQKO*n09ykxBwa+a%su6EyN zP1THRs7~)8E5U1)2q{c3)6lFX5^R0!HH&*CNc4=B#i*>lIix;zDMEg@KB1dbMThyj zuxO3ZBij+&Oa%v1wPB%*Nm56)8|jDJLm0>&maB{hvcu-W0&1@x?nmg`z*`FK4_Vr6 zUb>TDNvPMg2(>!8D*}2LHTw`2Ka``|VM`?6I(*3hW{ZA!2aYkoSK>K><69tAJQr{* z0I>4)!wBia)xdJ`JjZbu*h_UzkD%PAs@y}Nu9rp;fen(>$&?(IrQA1chM3Y@*cW6?zE%Z);bPFQY6)wI*HL|)! zLdo1zGZr=>UX4)0=n_3#@-{`Hx;RlX>Ebwo#L1tE8|=ib`u~Vy>{3M@6M9kffU!fXFGj^&^$ONlBkXl=<-YSA7_G)|D(@hl?#PrP15D89=) zL}gF5u8uGZ`Ii%~<@tgIK^E`t+I{ChY1c#5pEU_0%@hR5^hw4mE0=&Yq9%}_5gxhRD!Z%a$s za(WkGl*2KjIub^SoP6)$8meUR?kM?!7*R@uZFv(xS0XM9W6V3f0};5V*&;9s10>0j zntLdI5sOhvWiU{Za*xeF`c?YOs7(^44&U6XBFSv8>R@*XP)P5hUXQ#_;@d(j4J8HD zx6jUf86aW9?XWHyUq(c>*ma}M>_geT0h%iwp`rCN*`RbgO!x_Fx4cmg~sZtvH-Jx|lf#Z&X+`*<)`u62`Z_#gYB9%ZZE<4B;WT~07* z7pfdQr5Lo8@s%2x8SfsK1H5IV9EP;Vm4P?J6ON-cP)iEE0}Aw_{iM*Q{uC+~&^>+^ zXe1sg^;oEA^|Er%o=GpuNmoovx66?epkM9wmBUE&_gmXgb$97+>9zMz!}72CD7)w;S0qJlc;Ky^cu5?GCmGk1k3OUt~}s zOid(en7yfKG)KaFxN-){+JttSfl^Ljj%I%G6`Q2$4d7IioF1!nu2!o=qTLIt1fHD1FM()$Ec`bmWN z-oOzH$olV^S~%(gwFDpL(!ITM@k#j53f%c-jGZC)`VsvB&{8~IaiEi*^b`*=`s52> zxRf-M3BsDsnIM2^U6R5CrQxX+nBy8_8=}Bm>wY3J%J#`*@yx)H2FP0Bx^HkWyL_@h zJR@)*JWrO3hvs^+71)59yLUok@Uwm&!!FREEatizZB=0%_~l!nXG+WC4S5vAxI6O} z6+A`fZi0Y^ivQwjGB$bp2J6_QoC^{Ln*9DGr^)3470xXMQwlBqS1w9wDkTbQ>8{Uc#Agu% z(Bexv(c&xzpf1rnCOnx~61L$Xt`F&|)1jYLrp1>aj0I{S5${SA{pK0+{;aV=vMft` zKwU`CpsX(YWhVy0qb2Di=m4}8j145)E_|N?$>RAB2jjuBuf=m2$6vrwA?hCZ7Am5K zl+{1=(&iJSA1_6QpB)1Bh=*o;b_cjF*oS~)X{4q-&kxAL_|h00jR6jkxt3Ck=br<~ zRAgy=($5EFKZ$vVP;|xf9}8*fUg?LdvSn#JvFys~?_&^s;~hNp-+R~O;nr!3LN>I>KA&8Ffd?&C?Je_dJ zok-6!AWyDlc*Fppo+$pZ`rF~;;+_q4r27Sp_(CM^5gN(9kc(DQ0l^4HRS_CAoCw~- zrliOT3vv-?e9l>DMvRAyZOf}lEX)x(o1oUrha&$HxYu4Go1m7 zM7K%0sdn#}APcF!FdxxbgvmR$7OV%$;XP4_4ppq*E$we-acET?WXVKOCI|_ze92U} zm=loR(~hRT$^ngAjPO+9ioYvJW~?}fctE1pf#S&2LbOwXr^vkwf(y~6FOawwRi$$* ztOh^ENarZ%iPLM+IVQ_svk)nK(Fmvy!h#~P3*X0w=6lgzJhc1^dfW@valEdD^u!kn zfuGT43x^r0+cZftmJlYkFE%kP_#>8LGNjKGED@&{K-MPZ76T6@`{D|4wj!R^5>f+N ziUASQPBIxae6s=UTPIK#<><|lA}CORy=2L)COvb=OB(&fNYE_ zSVahYXsg>r=M^r0f}6(O7mAphxE4o{wg-S!=~^6$19MDE7Ed4!+FKirXVFs}26^dg8GPUsk|In;!|B!vPWM`S^cYhIs{~4Me zr~Ds1eIs2kQ=xC+)Jo9{JkbkrdM;{{ib|qsR)bdfaSEarF2ZpQkfU4+f57noklR@w zFTl=OI`2obg)Ml z6tYA;*{Vrdtj z(y*-=V*q2a>Yoeq6G^vJ{c~X7h`X#!TY}T0k-0DA>J;H^p*^}AE zH^tgK*_`sa+(#Glp1;13`6A!!Wh3&GYXp@%?XRztZr)7=J-575*vkEmwQdR+>RMN( znBDnnHr1S3s4HE1<}!c1U@^7`q;U(0HuE=swGIB;6ife>J;8rkg}ybzOMeC4`mz4H zKN%n>o;{WQ{O`%ey>IWM=JR*)zlT7@UbNbMYnFN~NfI>s$bXw8m^R6p{H@t5U=s9A z8(|sK%xE^)#Z5Dl^&!?z@@HA$mRTrzV{oT|-8BE|6p<@E+TOfkU%5JBfZXB`)5{-& zTk*rXZ*{WRwOiEs)-p@C2sub_Z`ma>{p&J<#83Bxb3N#2_(^}(-;C_t^%DX^Z0dFW z0|HyCTa*3t93CfScLF=pU?FETb_ZFxrkmmVA6D7vX35}?nG~Qle9p*cwVlplsvK?p zG)AGl53M@0I(<3tYl$wJ-$Nfty0@Mo=UZAm@Vh`;=K3g0E0+Z>bt%M*F)<{*@S3_s zKzFek>&h3yv9t?C)LjEQGk4w9-!9}rX}b{DI>MH^1GBVfgz$O)X*1Z%_@`BThFLy% z$3rlygG=8Pvp$+(Mpb@B;(J07x34I680b3V^%4}4?hENe$Op1bXYvKV@2Q9JT?=dd z3^SIqD>rAbEpU%j^*b}(gVNR_gvH;7T7$ncOXf}`-#2oG>G#Ke-Dv0Un^=p#Gpn&v z|LS*U4R0ME$G-nJCOJ;?YY@shJPO5F0L+a^u0T);CkRDh927Ry)%+5a$kPfU!HVTG zizoyMAmH1Zx*zJo&r}E!K%j!&BB)vnO`K5PW|Ln&db(d94ZGxv)qXKfH=iQ4o&s!kFlG(abbq?Me;8#T+yBn@ z%>g6o_@qCF^j#OrL1K6`_TGl7Td5USr7Q*1+>!`v+p1mH7oErDr@d+ zGcArj_aT@c=%&wXC>Pf!zddnXX4B4`69c|I3k9 z|GNZdI?5r}Z83;OjIkD^ndNH*Be_h1T zr*c#B55H=1*-f!`<>BtDX&=uq^kLY^3#Buv{dYg+aEcyAORA#31IyIudK5P~Z#Hyq z2q{0nZh?Y@^MwNHFI-!;1P;i%BN-1az9SZGA&@RhHJHUq) zo&IBcPv?QsMm-DTNfCa2l<{gw%cuRw)t!%@f;>?eJFl#R{QW!*^1~qCJDpW3txzX$ z+;E6*jez)we?p_aBwqHJWfR|4EDmEulPnfwWFII`##BRR3UADdWP7}^q7B9_!T!ET zSFE2{?oX;{dXG|aKlFFijH@vAgYo2y6(Z+Pzy@9$VO%W;?<1fO!3($%qlEM-N=BH% zn=IXYjdXDOYJ$*;zd0ZZ*2si8wFLffj@*3EMSQ9y6M60FU8jq#}pt{JPmM2rQ|M|1xz8_e*jC7 z;dThzf^5q#3Cj^qWNB?N4i?0LxQ{3oF$$ApS)#kdOLV4Y{Cb>d**)mkjDgjN@oMe- zoSgH0Q7>~!c{wGgFRF(t5M3fXW>vJ^7|PZH_ZoUNrdhvyAf$#sW;(DI9xaejd;UFq z45aS8;<=*f9uE*Co|8Ddg?WUF#IeV~SP+H}?s?cBV=TaY9+T600^!GV)?*rQJ_lM4V^Y_e|bIzPOb7t;+o9Oci*1^cz32d^wPc%dS$cITa zh;qnlhvtCc6=jzcYcR!tN%yKLfY}z#$ey@Qn%_lBtcFOub>1gnRwT+O@)-_!pFjX< z2^1;WPVTz|d073Daxz?MQZDqL@3Ll_J^*24!Y&yF?AoSX0&X4~2%T5hff;hq$tbIV zHg8f}->hqxUd}s9v6w-#(Vakum{T5A$!s`*3!Xt#WvqUb(Q`0 zHs8o#FA!Pn9rhnB0nX3JWx;Mrv^}K4{6vP`NKd_Do@F}j$Vke2w@d1 zMwDOUgj&m+>3sIcv+gM(rQ<2=Lxe*PSaE` z@jIp?dJE7F;=9^`uF>oFcyR;CLVjDdyjx&FmXwFeWcR7dFjl7WP|3E;3EiE|#|)V* zg+j!g!x(g2W#$CI5rOJ7i7=A9yKc*zFgWODR+Cw&A7#lbfcd=tO?7&L@E8hr=dc!I z#u9^bIQcZ9G7DG%cCkNp@Np0f}p3q!qf3Slm1%J0;C!dXWfBE{< zF4^hJEJhTZ>Gb3zXH(+#+Gsd3+%S;)5r(n&O^>=(uzUJjd(qRrEPjIJZuy#QzaU>#_6hmgYJT#R%(WCl6D1~v?8Ed0+kj(Sm zAv2p7Nn$E=-a*(x=CyTXj-;_j<5z^gF*RnglE_aFHZIEQr+ILfxFD1x`m-c^_oUs zjJRnUS?_qfyU~|k0*jnCZ|+`C+ba%v-sZejNYMovb&>ab(EZ!k*c$uw*CQ2n1&@PxFUH-@%&O(eydpy-9dIg~YdpjO)R} zYFG$-Q?7J9tZf9ZA5a!u55xi@%95vzQjm! z85HzfI{W{HDt3j#aj~Ro4Heik-06 z7Zo0LKnm@wz4#(u;8&`}k7R0+e7uhPnJ>Somh0~i)G{HPys#(_HRQac;02x#l9v;y zKs{ltB%+DDNZ1WCb>&Jb8|JH?zl#Ra#eb(*UVSJHyP719e{FU++6ve(x= zJa3Btu)o>Mp8p8tI{>41)GwT*?e;Hen?gvG1me+k1R;k`$G%48M&0SV-a%qnjb#tf zbuM8B(qg`gbvgE9QjoLj)iSq^NJo$+99xSA7NjX7ofO|JO|M&Vzn!@@(wUsL?K+b}x9P|>;skkP4xB+TGR zPtxZ~Ib90tl#2h_yN{!M@1#WR(&%A?T(&ZkUKZIJu^#$Oq{1HiFE9Gi!$&k~YE9b* zwcen3pGF+KU*sBxcj5;$M*l{0AmyEiO&eWCm;quU#gaVcLD-~I9mJw6Y)tz!?EZO1YxNx0%(LyNG&l##YjN( zw{An8L{!~O7h&oO!k47ZvpAc3DTg|OF|Ynn-Fiv_jJstL`U=7$OB{q>S|nuc!}}SW zXJR4|jhuui!*{M;R5eIX{T=UVY9_81%a=LN_fBm9%uvGM3*B;TZ))*rKJdAGZRSbH*wsf)`?xA6Pr?DXY&>d9p8_D8~2Nidna;iKuc+emX8L3tVpuS|bW z=|wkSl5~4D)Zj1)Zj%T_)#+4f=Mygy^|L=Ck9v{dF{~) zbr(`(L|ah-<9$??E?_Lst0~&*91z$|yQ#c}S}O(iD@;7qgm`uP2Z4i0)r5+O=}ZV` zfqMycGkS~o5rTYyeXJU0#kxS8xzZfV$sCBB2(`_n?LZf5%xn_V;anq5#L-6txv)dA zYJ~Vu8V68DA2F4Z=tbmF!bVASB?19sp2bKReVUBDs;k!ev`0;iJ^%)v+BN$*)m_qJ zjFA;p%ib$B((kpj{43#4?f}Nb%K-S~FILUe_)D`A$p||dtBSz?mFwN6-6IXv-A6BD z%B2^ksGK~O%Z8FY^_V=tM(9{QlvJf`>hzZKRop(RdJ=6>#oc=$Eppd!Or>yzPd2v+ z>$=blcZlUOr|Qn=yZ1wWxXh`#pHCRUuFZj3s}Vbqa8Eb1bQq7RIDiFsL7EG=Rs*h; z{>_AXd6|iZI)ggXLr|GG-4_Tf(7o(_@Q{T=6YAA#S;^F8+tqTgy{ze=B&32Y7G>(K zs2nMz-^-c+!R+eJ>>4T0TeKKsg^t?s@!ExQs(pMD#cV9QU&nkszMTkjwmVlcJ;z~T zccy3eT@y&K7L64x7h`x|eT{1;$D5fQ4#aBV&)8}TE2FMNH8?H5qvbb}0468hDpr@! zTWkU$p6g1$47%;OOK`jymkNq&Y^|L68{XHn1FA}l37$PblEz{m{P*J|DxY43K39K4 zg;T`W%d}`_g;rPplYU@RY?>ra6Dc7S)j=$tHh^%I(fksc<})o3!mv{binnihU)$%h zBlbob+#pPp11jNk>|KOA4IXgPJ?3Z0gqBaEEJ1yXLUTGo?Zk3tz(Iiny4c61yD+H0 z)3z%1ZNi(3D8HKi&&oZOFpeG4!kwX1rchExTx*Z!L%^vVLW|=Bnctm2jsE|mmSi>oADC9I&f#yfQ4z`?^Z2l-WJC_QZ8R}wzFK1JlT+OL*R;47xZ z7$?$)@8EUb2CJ%j7(o?R1JGNLB_&PA)ypX4(D;^{0!mWVyKAERba)(5xTkSF%4Mn!e^vrATzIjAobpAvueg;?}}CPhAGQ$ zY7oI5&vuO`X1#CVrwaK|#$(Xx4XD}mM9o6_7bAjun-gk?Yp2v3r|5g)Ir<$);2OAh zQY4XygmGd-NaeLuHo|LD^TVY7$WVha>XbUjW=kc(_Wsm~VEe{n8e0MOGL^0&TrEb! z1dR^mhXv~-d%+t%UvR=MbS8e4H@*gMUJ`*0qUL_t;pwTjbbpI6@rYq~G|T_g0@Ieh z2gKV5Hz9sYKa-D>7xO{QNrf8O*LtcXnWVbrlhk~SX%;SzbS(Xh)cb_DCBaWTE6i6F z|IH~RY3a|n!}6pG0D&#!aLuIlmWd=kB5F5nD7M5#bd zq#S^UH9)kx0Fgi1@dA&ImzVNa7NKckvTxNyCo1%qu`7BkQY;P)}E~CD_O}Pb6f@_wSND z>yR!_giQ?z_c6I67E3?V;Q5uO?457%@MP~6WBMV$)@!h}Ov4ry^q!9qHc5hIxhHJz z`2y3gWs3ZERB@?0K;gp*nu6V*eP!_{bF6&5NIAGyqX=SKq#sNwYhPHXxVC7Qnk3Wsx|Y zFfq-ojGCtjpGLrQ1pVz)L6{S(LXFpx_yhK{1`cj5#^4#pX<&Ljf=DL5BMGEG z@n3|KvYboHCA2hr_oQa&<(%RqW_?nG+b{_&m6Yj%Ou}+cn&hmM(vXGHw=1>pU{=a^ zlBCXClrJD$+_t{YtQI^epCbN%6uA>La|c=Ct(GnJkl$>vCzeo*7H~|ucfJPZ#6`fX zB$P`6u}@-ZC;eU$w-dn}OOnT&Caxv&0pSp;YT}JD31+dop7;h`^Ax^&*HgcK1{7vx z%izi8ux+{fI%&r?Lk~8k@(a$jAW<&#{L4WkuW68>IN?)CKS^Q~3CZXqiOED*7J8Mi z5!L%hnhITBj#U@yEto*trfK|EtP0L&5B8U*Lk%&NdaVwD+)84zBm_1(saF$^ORJ_5 zfPKu0+Jpw|^&#Oe44`B`8M0{uYwqEM=C+(2=BJNLr8_opB?k@l{A{U>cs1>o+Ci$z zB;gq_PW}Mx{Kod7;gHZ~W1q}KNe)9QbvZFPdq;tO^W=L!#Y{Gqqt-F>gpPbz^Q{PW)T4iV^>JWdiH z6S<7A00Gra_tVtyALGtwsNt;?-yCXKPhx$j;Zr2GV1M$PCiRGC5a2Dho(sBD+yFx3M)`bDO}mJmFXGi$^*F44cdax4Zptpg4tYLW0H^$k@LCA)rzAcO0sc3M9~eQzt0@AV zVN@(}hk5k*ZwT6CJW$Ka^<4R0Q>c1PS_Q0ceccbE_VyUL$Z{+RUOoWPDjzPKA z_U@Z%pm2;QhsdmSjH-7}2p9E{s&)WKF$}J#K?go-*SzbC4_-$fnTDaXllZ+KfIsav zV8gyt-WB1QwuT5~NS)7J^J(?+u?5XK?PanaAk_27(lmjw5}b{QuX?3GnD?4OU|yn% zP6!y7pZbB6z}3|M5TO1D;2ElS0zrN;C*rFV0S;lV#0qzXFwLdQs$A)UqN9eF`?kgJ z`ErBXTa2r+pizM^`u$&kJ{7IedpKc$B$|n=BdiAVsa$dKOg&2KB!MlSXX*(qLe>mDxU zsWcX~v_~Y-gUGvtw_r*o7R0naQ*EEqScXzrE_rTRGOpTB#&z~(@B1>wF}BJ|hMXv4 zJ6z_DEnSc~D*Y$IsWcL|>IyjV zbhQpoF_8|#3*%5a#jR3FHmeh%<*@2scbdQ=jTJ^JxVFYgDk>FoIk5**nh zMR1jmy)fb$c3Kaw)uR?OO-5bQN;`;h4HDkCinekw#Iph~m}K%e^5X$A#qr~f=%(+X zUbA0n>RL3#3i^m6<`oBsYg&D{i=lpm(&`f+u(*3Zx{3(W+lR&7^U+O2iU^Zn<41oW zQck$M3E%2Rcie1D;Ax?%0KtTpvl_)Eg zeDcP&G>HD;!wCLIm&(Ugp>bk(^}*@H$cStvY-CD&^oP-WFi5AdZl&lgSY9h)ie^&_1Y|KL1YRWn?0uRDpuJVh0M6hJtQx8wH)T zOToV3hU#kqUv?@QS-rxq(kpx????!C1P_o#x^GwNc7YBDo|BJeh_NcT#-)Zg!dni>Crs~II zcC(=~;Szb2?!ZmRw5 z60oY4+T#iNl6aBGbR$P)OqJBrq{;{)E!FTkV>yBSfvUbglo0f`f#*f`Up{7m6Kt9_ z75Ee%^ex;#03$)`ue3qjGO9F2i7sDR!ksppJE*(5ffZJvW^Bf-ROaR$Fme>+X9XM>%o)j)VaQcyAjwUDu`tr?nxW-XxRPf+*K?5J!j z#j%`$JZ4}XIjs(mctf`m(c>IZf9h8EORFVRl=G2{BZLn^D4sxYGyZ`u-up~di?P}- zIP$5RH3XyynlR%>!VgI#?hUWF*SzvxlL|1hdRB{3yMwr1M?Reh#j2MWDwB;urh;PC zSIft$p3oZqBQ;@U)Yycp;o~Q{u;k{cR2|G2PDKJj)hiwdDIo!V0+aWxU-AyW+U>QzM0q?uQ_Gn8hKNjEkVuH!oT zCy%*Q%9N@+CaoEn`9CD?l9gMgb?s?GF^*{otAeoplTSh|Hc*Kdb5Ig$_82)^47o)v z0&shb5SLG;ZNiWCWX9bK!+#x12ZSVvp!6g)PL5yagHyg~wM z!5eowk!uO%^cIKN(r+WdcGuIoZNdva8CyTKo56i{Bs93!srQ$=*Y!G%ZJQqP zHhuEgu4qnrUdXmNE*m)tOKcVhNA*4@{Xh{*Ik@mX% zZOf|K6C%L@y{y)qfdB2cbJy!B+1SuxJg}BN4h#Q3w~+cqjIsVq$^Je`yg;NUA&#lp z_y+veEwCwRCzfS?tVf(9$Nnkh*rBw*1=jcmUOTw-pebEaEH8Bo3u2BI>=*7m2%x4^ z@;?--%XzQE3eE*W@7H)Ry925Phc(9^z{q)?R}39HCv8?}x5NX-#8g0#Jp=D)F`l5J zBgmg$Dc6n$KIBN{Ra}Vz~PCL4sM^b;ONWD7ie$T6Rn{mPsa-? zj!cigzoyorH(CtrI>T9szKBEDEP5YstS$XVN#Yoh#e|D5BthjTnZ3@ZQP$Hlx&XhJ zQ|UG;-4be!Cg}eMFnSWIEQNue>&=YgTYa*KC$OF}ykB_iRiAV1t3r}4!c%0t=+*!H zi?+4In}vygPXg{46PUtCo@rNSdU@8Rz|1oP$as%%5I&vBo6kpA#j>f}3oIkhCz{`~ z0fwyXXPD~!zb4?qb^!j{n5Fvvh|UW15eU6pm;=J8bXGdbgV1=3p4=x`%81yW{iy}) zWzR-g=tP4eo=4|IgQB19rC;E%&FV0)3yZ)7X@0VSvk`qLu`b0CS1iF@1&BkVjP&bP zk+-GtAq#FJzhSHzrN&AcXy|RC1e0ET2Z^f zX#g&X0nebgcAp2N>kRMlxl(_KAOaDdEfa4Vx%@^w8!$4Nv+hGY&tjTyewecJ9NJ0A zNHnLFj!5^?*)T#Aq>!Lf& z8nX?98}*EP_K=Igw*SU}b2`EPgxfdQ7u#pCxC@_U&1+f$2OARwM0;`D06cdWaeAh~ z>;lC-VT|F+?)Lv2_a#Q`+Tp+S9ubaz&p3Zdu|HDt2 zuvBP%ou)bZhT%M2E*0!eUviBGi!^XQGEyOe@OuOS0F43wYW_pNCDzevj8X_uB;9I4 zu+P&cCp=iCb{7M5pBM;%KgN1)N9Ilp5}Es>@aPg(=5}9!5Pd^9F@fBbv_J<1xz^?V z#|f!-Bb|%9-9Z&B@d2l0D6K9g;@PEKMgk`A<{8$ssoUk3Nb^~Z8%t$n;}cfIs&fHd zNCtM9{^V9ztcRQ5-mg$s__Pd_6-Vgq%_4LLL#4Kwldpy@QB;$5mLx2B= z@4VEfF@(i~^VNMW=`YYfZv6Byhw5MLu?K(Ui=T+@ACJcSN;|6j^Mx_bFjk$XN8>Nk zd4+D?zhs2|i*;TNeQ$?|T&DkucN#vkzQa0#8%aAlr|`z9XQMRD_r&r$7O0341~3J7 zWMN@uT_B0xM6M((6K>!P^-N*i$hwDc7yHF2Qy3XyOWB*r_rkTWCrG2Y6f>W)unIj> zPy@j%Aqp2eJX01E5pumM@YpMOrmQ0MF5zw9iSM;hzh5NqdGehi1z*xgT(4PwCw#8w z6m-!qvutEg;)DI#qX)iL2Hx1@xM08TEqSt(is0H$obW}2Hy+Ub*S#lvi3tH|N)@@A zCGDQx;?Vx<6?V%B&VQcSWM~$B{W~hj?7%B0^k-F)b^L451UfsP9=s7;pOI2`K7Rd% zbnYyhJh;Mxg}U^l3-Xgt+ftbj&l-{)$|?sPWHY#{lF8H{3;LvY8P#dB^}*mjtDwiE zFQCOwgp-o^kD<=XB&3j-%nEegOu`f>J2{%jEd)0Gp2>+so+3Oh390`N!f|m&P5MrW ztu0S{?uH!lIgS2|u9I;9eDizkD<1n)=h5MLr*y@1T@eFMsvBn4$pfi}H$p!GI?!XN zftnn6qGvp^q)TahHrdn7E@3Z{6BN-N5Ap)j@Z4+O|#Mb zh6~lZ4~2B6HjOmdH_0;SdF7oJO%jfMa6+CU8VY9rfIq5`*Z&$HCW`dY znMoEto5$e4U=-AI@xv-dK2GM^oBrwR8~m^ySQm+HvKGVp%qX59*C$}DKcjuuR&g#q zVKn*c<+LI3nmBVOlkj8%U7uk*a>jR35%Fg;o1Y{hXyMCQyEti2J^zhsE|)X;?x zMq)6|+^P(3>))AR!9@1d^uwr56ZoYO^$<4F8N8j;{5j6t#T5ItfSQRvQ}JO|h(WB} zgSw(kAMS(%3nu#wy^4G*{!$s~v}zT;^j@?4UelYsEko#Z<_Oi<<$WuU!#P(F*7*g| z;IF99hkME-Btv8R=#?!-t10~_g~ge2y7KLp^q&+4K^|RoF&{7K3d|@fcm~TM(xV5# zDTB~Q5+8*U^vsnKO{Q=4iTes!r_8RKYAOY*)&e8RDLepL9)FFXu_90KYAu##_QG%R zf8XDt{MoX@PU;$GqL94bHwiwxdq-{gBVCOp3`U=CfxJmP<@#MeDS4jUm4s`dzNfIY zMe(==!ku(MoZc0E*MN*&fNmZ1Ai)-Z7a(I7#FN@d*v`Sn1?l8o%3kC<{BWKN`jFj3 z*u(p$EhdQkGOvCwItVL^aZW_5xbHaU*ZEY-T$)$LsTU%F@I?+8OQjpG7pfNK$)G2 z_*f^Yc|_QkWp9(j_$nz&g{1dyi8EpHAFrkd?+~lo{msPKekB8-Q;5SWd9@y z06(e@mj1p@vAXpkpu2>Qp_`b4W5F3C1gw{-k&sVv8^H+@u-gmzTyVk3S^X zH%Si+A!s9ty_kUi8YY-9vLP54Loy|SsW$}eJ){7boE7%M`=6l({AP z#fvmgpfjN$P1L>j`Uxs`$oL z!v~~x5FV4n---N*@CszZU19W^1J$0Cl7Ex?DdDKh>4$h@e%MO-Wg!20o8pK36#bL% zwI0PjcNF@Sp0_b0prn3Ftuhb8w`vfzWaziKB}^}~Ys3xDO|smc8BcT zAAJMyn@BO$MFY*rY+Hx6hGnpX8D4%>T^Y!(^T`9%-pAShoRSL>ywySt`I+!U!zBUQIR%9AlDLZqb313QBsLIXI_F%2)Tjc8sg?STEQgYD#NY?!;fF$t z>i1P^>KP{`n~v1q?M0_}A+~YaP>+bW2JzIBXv6cM0^?36(Z`0DfL}}CFj;MQi^v}c zcRS=U&Z2Z$nC4i!)a}gjUUkq`5p;q#hbaq?EvBL_L&C1ls0aAxwCho*udmkAme)oDb3wT zxL*?Y5ZOm)l0*ZMj|hK}#N&n<+L_>y#2zB!2{OeVdW{H(4ZWQHA38)QZyVm96p|_bW^Nn-q&NTQ*pAJ)pDde~U&gD5%=%)vTJ)siergPAIPI%4) z`e}E^a+q(NE0vb^)Ss|PIKF{?9I^HKTb$=jtni=ZjOMAW^uLaPNv@u{pU58xj|yS_ zhGKu}=`=oQdhV1M@$zZJ#}$bOOSgQ1%Fl;dqDO|l&bXd>n#g+uM$QE1g`nU!4+MW+ z+KwPnCPl}cq5XVk#}XMDN3~LZ+8Nr&_E32(X7dgbR=?9o5_;?hP*vSTHeKH9iF zcaUka)xP0pUtaLn+79!5_7+e?V%Bb8T|OWdWUsH|%-68k{=@YawRQ`mKmvz;Aq4rF zv8`on!z_aM_BQ0`RxAn0;c@0wVi9~ns|^z=A4HJX`X`6eYQs!Yk~=P}AE^bzSlEZn zWgNeP8Y;_s`ei|Wg`TrvjG8xk%{|R7`z1icfKFi&oxkQ;7bD;Dwhlptm2DWz8z$#~ zYgyTbJwn(diJyt=CWv&eGl~3_@GiT+v&`8?Z)YJ3FEG>tOx0iJ=#HaA-qz{WD!kY& zspKdfoVeewZEb199J-IAjbCbAY$cCvrHtU0ddYJ^U{UD29Xe01sp0JDMPdK4zeBxE zZk}FEvF8QeK4)|A%g@xaSNy_z$-@tEJAW``zGk5eMJn zLT45%K!%8g$!Fp?Ske>m<}>$*XkUkE<{%3tAv(!%6`lJ|&6(Mq=n-&Lp= zZa&b9f6Z@usjAcs!qZ)Bn?u-ENbT?}A)*7r5x*_pfGNj>Ae9qmGKajalSFoMz;4TF zlq0V`P{ZTUCR>@n$=IxC#CY5qK`w$e{5+B2t>=SCy{tdUQ{xn`a`m?MLmIbUK!q`c zQ36rU;kM2pRYWM1#9|@{{B+A1aaU*)T#R#cbITOReD@>yV&1pPPT1|` zCdRKfLj^L{$%DKUZRH37P)gtX8F;|RaGS94)-b0wlTPD~+EK6jMA_{AicqV=w zpXO#Yi*XXcrxDZh#ZHhp;-GV`@$2vq*h7p>MI50)Uk&Qa`z@+FV>Px3sI%81CuhEd zIR8TkO5$Hc_7j>QTWc`N(KoH}&CxC#N*hRbF>O*A$@3Ik7;_>rcqUN6OoxRV*_~e{v`o9rT5>-k*(U|NHoT zR2oXiVIsxy43K_Zv^N3`AOaR!;T(LN#gl;rdM#3+&jWpc(Q%Bc#43^{b7vf-?+qF1kn6IWce=+b3ZuQ;!~vU1UR*AF{y@OeuI zFTVWJ%Z3lT+}?WD*C%`wzt*|Mun(Q}CHqFht;dRM?RU@m670Y^UzqPii@IuCv&SBH z&etQti$yf%widN*p*{ATFC}bei@KVhFF)ry&q{%iWriA;|J(!Td=V-^r&WWzJHrz| zaTzGmycvY4_r2ubk3DOIL)mek*mwE;zXtgo$}s(+$p>51t-1^r8|IKMSMjW)VLV8? zTL{;7Sz;u+X$-DB`SD(J2u?*ki=bv8i|24lBaVG_BLuH#>U87C9%;^=XWLX ztt6fxaW6q$on-DN@+9FANg$5re@FO65}2TJD0u!kN&J-r0yHX`o+Hg;QDps-k?gX6 z@Aanzb0|5FYGK%Fqh=ANN`hgGf}o?WkVIc1U@_``Nem(KB4M{AP@JPaC44N2DMbEF z_?GRv5uVhdR%b!0Q8-)o(sOuk$qt)K_71Z1$$pn?dywf*4USGT)aV`pu~ViMsj(>w{SM)O3c5gs7&DB(fFPd%PS%wEXO^ImCm71?avNB>C@Hxc=o@OMdI z!H!{+V?2`h1Ca@Y5hQ|P4->nFuvCg5@)!hR%zcvBL*#kF(?)=P2hme4+2Pq_9U%zw z!p|r21>qRTgioT%dT5+1B^Q#5vKS*=37>1IvB`uuNh~IUl#ZQ9VsJS2_E^vxTP;Pa zh%^$Oki?xtAo18El7RJN&k{~O7Yy?&uT=|z7+?i0&mr6qDyY+1pyx3kT!GG?XsG<5 zU9JxabpGR{xAwTfP+@)~Rb)Q@b;8R?syVMk-MN-bBl|BZEJDjT=;Sko{BK08S1^^b z2)!i%Z^qRS%6m%vFvE_H@W%wlBW&XXplj|T!aNu+Ke1!JK;!{}4AFeUP!swQnCHCN zg2*!jZW{*`3HNs@e=aT05Mew8 z-;)UU@$wum37N6s-AF*hi7}EuVkTn6OzbC#0Yok!h}48*@JzgfP^xDZTpNrF*Q>{P zAK5n(R#7i(GZDmS;_Z@Hw38SLX5v~&A=wijCv24jLNF0EGEqZ-DHA^?h=7NELgahG z*Lo7np{EqGMW81I2vL%_lt>yO+0Kvhcb_$k=n%=jg$UDNl1BM=M9?dfmPyG5BFwc( znE)-v?XN1Hykl-?@fiWwoeBC7<-8<$Sa3JxY81UCDPL=_bAl z2%$fgU0%-jjH%PIh{4^$P$43RCqycI7^?7CF%_JIQ_CsG3on1N(1Cn`Q=~to+%!@{ z<=Ym*$d&)Ad{U_V>K4Q3n%P!9%g_jz#ucFox28~7)>gqS_msAkS3_9;f0tW4Hz|M2 zq}u1QGW_3sVD?f*lzsQF{y2N;S%11c;;cW>tMW!aS9R8ZwSRPJj3;{|sQY=VcWF#` z*P5DFZ(*g->8dEt;P!ZU*E(GWbft{&7GuC+JH==92y$c`-gS*r<^j$CW_E=O!tYnC zNE(_yD!gm016^um#r(Ea7Sn}Rx+t9@r>ddTnNlRZGUOH&Rc7)?)N-@DQK{Kxv)$yg zqE(swmd_fi((OpU^@bYt+Z>1`iRrlH(ib`@J0ikbt}5;8BCLL% zF17Zi2H6Yk!y~)L+#b#bUzh`CU7loR2 zUAT~_7W1x^W$JPVSl7xTJ%-oZlA0oQ8BtA{u4_%+y0yfN8RarSbzM73EipWTP*n48 z2(F?I2vtpE4rPJN8Wq0DP#H^56%MRGv1#P%p5~hQrPK_U`4%WH)p_$mT?SxAyl0JJ z#yfbsm;}n~2coQg!9Z;~{fhu7qAud5B)go>4)rL~rvgT1D6Pv$i}ba{kwP z=U|Bnb-=g?3S;`KPAR{UCDke2Kq4Jk(qqu0HD({MD>_-D6HH-6iCM74R2YWBD5rdm zX}{LV$_dWdS0?jCm}2}kKHs<6$WPDj+g3QAfrsh=O#YZObM~7?c6gO>htYeZnLXF# zsxdb=w+*hu8M$sdH#aAIwfvT~lMYfb$5-32BQm-m;SE2n_el4vjl{O5zX92f!rTG~ z8A8FI1`1AB&1H(?G5$bvZnMcd)s~s*^DThyZVn%;!R9gSxy|#1!n^lVQzy%WRBp2) z2u%od{^T~$N9?RFR=g^=Cw8&Mj9Qq2IBBL|6~BZ&wRlK0L?v|zPw0136=t^*VV3*H z)Gs?>zud*@U09kJYPP_P_lRk$Rgzi&|NLe|p|R2Q$MYT7Xm~{`s6{GDC=>bv+CtOr z7qC`MTw?NqxXa9_cy*;2pT+#i+ToEQHZ+;7*5L>z8fr%mdaf-*x1>-vC94xv_h#E) z2dtjs_nBKFTAddA+!oRiswA5pviCV~cbRJJocWS1YqyR8%-85_?ia z<>>AfRoJ(7wNlTo1Xn5A#So)X)YWE-?|@%jt^1;TeLRY?JT4g7Ol0Lqpf}_#r`hZ>dRO6 zZq{hE$)45Cl2@5t*A1u@c2hTN5ML*{SqtYKFpV0IDl=<5>pdJih6c14suu&?vo~g= z*&JCbqmualE)R4lhoSPvcox^%n`5l*!vu_QXlIp%;zUD0mgS?vJ!@i6;@&mDb~W$f z!}iG-tEW0_`@36x)Ny-YcPo(}ZZHak3Tq;FL^_jEcZ65OC{&l|Y;{Sf`y~PN9=WxV z4Uyz}9Qj*PVc*l;>N&f@G%B|zA}#(XN?RtPzW*1bh&Hcg;F3@k)5UKW3(Tw?uO{<%SE)c@T z=jwH`y`DRwbiI%=9MP$1dRV>WJJ`d@D1fp>YLM<-gzW2Dp-@r!az zW4+nw1|z~#+KZO237{S3q5sO_xvO?0A4o(kn)Zu{RvHrfd7@Ru*O(+Li?1u3ulgkG zQkD7KnIwx--b_0z*&3j-?ZL@b_lf&N50sm&J?at|pNq!S^`h`g%)0n3@$IYH)Osd3 zqEj2cDIVdGx|$tR>+SzZwvtt|y*=6L#Vb_*oQ!HcX#bFGCFC5GHnYs!QGBqIb*yPs z+Ef5st)q#0sxY2I%$=5Jk4Zs3M%nXItb{^DGr&Y=b)9XlS9q*Z8p7^rOMLPOKFuKL zG!&Kya3DrAMv9Nes5#htp$eny7gMa%&bpC1$Z4nTlPO5xv{t)QD)Pbiv{b84&tq$q z9y3s;A73M4=Qfyr+`c>2ie>THnrfv4S#<(nZU!HUx`Nt|&m$U*OC{ zS4MQUVCom5>&a5QFF(YJ;v?x^>s*@z7Pp!>odL2-9rd`l>JiK^?00)x{4$UIeQ)$Q zg4&Hqzp@5btVRuT&8UmxJ)nFy;>dP7no%e$(F2*W$@Emoj*l8MopiI?+YKAl)=5*HIgqd4=V{XiNhp0u0Ntkj$qF@`J50%ogO zv4TEaTVxi}g0W^+0;xnZ&22xc!j4L}Qu@Nbh0>-vgnG$-%{C!_hy^9h_RMsvckqBP zPaA_JTM{)mkR&QHt%q8&EglN#8nar9LN$m=4X0tDn4n=YppwFz_J&m=;d-1UYp@E{ zsWv8+oHmUnFGJXG|Cnxd_ms5S5q+$IWhF4L(Th0l^Byr_u>8qhSIuTfg&0SLb3Ii7 zbG>(?mv3FH2{#*su6LuY>LAObdDyeb>#mYAU;z+sukT}}1_S9Kn#?UJ25b4Fcy6i5 zQ-#aSQAHgUgXF?~GbYPZ6(eP?B+NZrNmExk=N>kVWRcF~N{um8n0tiT4q%BMjnnca z(Nmc!-i1(FmhX11yDwd67xiT;zX?zXWwI;(E^-%=?FTtMEta*J1d^SM>eIE$|0{c` zlf6{3>HET_beS(=WJuH*cL;pEX$+#*K^69Kkl;niXZu=lJ#=olIjBxC89RWFw+Cie zaaW+%iqsslWC?021SnsaG|MbrsOP30*@figqf5whDRN;kn$AU~Jj$6-y6Tiu&Xnwy z-o6gQnklF3T^Uwvye=&_b!j^~lzu9uhyL%jhn%*$^nbTKWRJ|mOgd`M&b0ajkGfPo zB?E3J^9AG&g3N<-etXYO{!%wz6WSSuVe;Z4WP6J`q%{DOPKLoUO!gdelRC&3V%;%& zmbuCPCDV!u#t3mdCVZO0uCBz5ZjtDJ{{eI|hC~spD+1&!^Pt-A(WEo?d)703%=PAe z)6Fm&!pLX{YYKBRn!*l~p+UNEW+7p-f`_HNeelx3ECxSI=eG~u$zST`w-0_YVsTnV zxyG|8tX(oD7nwW@j~KM7wPEdxiew)bJ4k1X9fW-kIu|55i*B9TqOB%(TJ^G6Vyy@C2+;T5f0cx6&~nX7smG$K}D;EZSr- zZ_UbTO&6CS#)v4>$YmJ0lih{D7?Ekhnb055`k%C)1RS0uXZW})6RgOIC($+Ei+u+sXD63{X^jRB>m*n>J+rmZ;{v>@Mqlzq_vD`_MQ z5tpGE1L4H+M5F#SrLJkGqq3a2xp+YMkxY+nUvEE6dr!Y+`_KTZ$FOSIPb@c6GM#bj z1t}`Va7ZR@(Pwu^(J%Kks~fmSv@k>VtkYqt+PyY%wTz zSQwP%gcynX*DE|T?fD^4A$Wb`?eovG1_d)kEiE?-OP!(0LS@{ZX-59e7?LhNjwqT-Aa!H%NKC{blWy{7oc zv?Mg7J4;uFh7=VNw>uA*#$sGpD^Ubjx`+Z+^__TQm(oI_N6oMmMy>pUiJR0& z>k-t+w%EbH&eV*haj8)kFafRALtTtyI}&=bjd6Em_Y5)aj%*kQha47UBaSZZM|GhJ zrhZ5>tUh7iJ=jW_V+cK0YoCLr5dDT02xD~$jsgUF)B(|M2()Z{$Zb}RB9O!mS-K!} z=+6sPS6AES5NlxWLep4{32fFXh=XY@9jq=lg#|ha<8YIMmGjSOdHE1)2)_Y&{}3xL zxO$V{GsJO08^mcSPIY7Twssghw7%4M9dL%Osk8b(+t61Z4rr2S8C-o>=%A|%Pj7H` z0YDs{eoSLX30Mp-6H|MM8CkUiC3)54jAW|ptoJ5pPq&mizF}#Mg zZ?QwN@mc!7N0X1Z9O{S);m?uv*VmW^i{5gqhZB}(U72~pYOy?b+keTiB0aCKv5)3h z$rIja5RE(6j9>2)zlk2h8`;fr)}xghKs>mhvJ8Tv5)j4w&|sTx%e^ z?kjVdQ33n+xz?!6_!>|6?0d~Q`jroQ_-{7JGC12TTdA!#H_!ed*Xr$gv&rs0)Vi{d z)JelMr;fWGy?H<^OlMaUYVyaS)`Xx`4TP#{x%_iXqGy;(CgQ?YVIWce?eP#Xh~r)7 zym>0dy8<<&$NJ`J$0I|{H}&FzmAN+7=U= zsl+TQW_>GO&o0CPnzmytI}hTzak^=59)@2c%5ENJ4P(oEW*A=AO1sZ+{WX2KmFaoq zfPLd|E3r$=E{|rfZunY(y=AzSa`9`sK+3Zs-Sq3#Pv-t>yP`s860bGkD${*A-SuAU zuAzC}yH;*A^IAhjgbb{AE1P8IIt#h(qW3mCHP7l9?0ukFG3B%@_1>1^`JJm>(`x)7 z)sR*yUFsSVUCjIfphR6tr!5gzh$gW_F&K1(@)%t~cbCeFkr(l>$dmpv^A0L`HHiH} zo)tMQwbe9gK+jyG@Mtb}1TeMvplU;>cSXRnI>pgLsSB5QS1)wgNJ8W3fMvEll4Zw? zfWBGwm=RWg&m&Ft@)1@>T(N1y!K=arqF?kMbsyblZySMg)3o0lf#WFO{&obW!xFpO zNGoGV1&0?@WT;zea)^pSZ9KBg(8|hxSJO2$D=O-D#(GaqpXtuwr0zF0%kccpMLdN4Rty9!o6PlD z%=H~wt=j(ct)2rZ8i1-Cu+59AZ8OD#B;Py6Bs*Qu#P0~~@yI<612cY&yHdFI$M3ScjN~LY3uaOx|z$lI$o@kCJRK*V0d>=8|JAZ#w3hsRx}M zm7aWz&dFcK6p(z{igj)H9o@Y--cB8DB`-?kyv5n<8k?(3Gr~ON3Op&fa<2t*b1g0r z^W9KMtzRBy)|hNQoGD9PDBd>n@^Jps$S$}@C)hlUxIefwBe|r5JHX=H)b8=8crfGZOD-ln?Y*F&dryxhu79i;3~hM$6tk#!AJ#@ZlKd?MnOf7;6+?L&u_KtL@UUR-&h% z(Y~Ifd_OSO${JsA2)2h~`*Q$m=1y>9j>r}q%?$T`-!kEVOR5fOjjG@};ehSW$3@C_ zzkFP~>2_hhmBUwczLgSJxFnpL1^R4P@5uD;WQo0boE7KUcFf*8&Poh2 z&oMhn%tIc>QHdad@*kO-=oWK_r^zFoxVbfQ=VG?m;MvwDTYs!$4l|_@iEXAnYxawH z{P9Ghx$Xdyy*+jGH>qPzDoa-l-(;wL1xRG5Y5o+ZzQb%_J1pOpXWu&B8mIE?*T$os zOYLvRTj$NINE9*EYs=#mi6Jq?u>9}5bFB_T9AGKvlQIOQY?G`SqWYXgK(hb>q*U4~ zCRj@+t#mA}@=CoZY2A{-#7&`5N+wOfjsMaB+Q;NfG>kC#{z_B(G9+51?OaDs)d$4A#>3WC8!pC=S|6}yOtj7mmWpSP-McJfx`+Be zp=>=vc+*Pj3~~C4xh~@iK`L~owH^)&h_mKW;{j^uJ;2KLiSN=`isWP^s~ozPSK7Z$ zw7SKYuxNwt12rKSnj{Y-+L@EA#PdTf3P1sR2?}i;gjO0s;Q>}CPzdKJggP`}B9`pq6 zzK**J5^Xyo;?&Xn+goG`hlQ1Ui$nW4WA8DO3Qq3vw(Z;tdGxc{?S}*ly7Ut?UdZdP zeIe1-5%JKf|KiGk*E;F$`bb~`=o$JV`&HQuh7!^?6c2gIHH9^GZiPF!h*d79)3eMY z>N9pE_O1&!k_*_MTwsmlra}A^t50sRX>iyy+uS6x*f|Y1Zml+D^^>9A3hS|3+y3BQ zaJ_xa6l<7=@5iTD=Vt*yY&+b@BK5lSnsn1RAdEdKho(%%P_`Yo(CQwnStF-Yv*e(s*nKdNnxdy!>)4s5{tzBuLW4qFTmLdU2lp$DcYu+sjUD03b zvA0~v>b1~*{X#1}c*|Oo*EryYJf%2+4;{-Jw=|drl$*`@F3wQb6bP1aTB}I`GXLGs z9iuel977IZI#H zUT+@IDo=o{YMI3%py91*Ij(1thFx>OD{?QQc1ui~2t^52CIA+5&aCL<*Y6v<&-)pg==0kz3iN{V#YkGHnDWv>4=CD#mjiX_@5Iw^j8 z!E`G&!H=s%1`z+>1MmyHLDT&0=o0OSE>FRQ&}G^0h_2uGFLa@aXlD8pqS*b4t?15v zOll#PE{L};D7F%#6GJ4I0vc?OvTrEHjTU8ZEVk0dGxb<%%;Ig1-NZn*mx?mhEZx4% z#0v4sj~Iv2UjJUvf6kwPJ8qjZtdyzS**v7d&9wTq{ju(DN0jX8XPG5(1gC-Q$Z~*= zaeMyao&BPm6Vvn7vGq0P; zDp6u@B-8V9y?t;d`|p5huq8DY9`mS6WepRs`*+FqXO5XTTyu<(?%y;y z(XWUd;-LBpH*qm>#nqv;<}11Bp`!gY@gZF%(TnIqAl9+mSc5&i_S>IQ z+0(1pwq{#HJ-rUu`Lo%N_d0A>%w}orbHG-c=@=Rknv!n$3>2fY+``tv?9mX`L2?ZB3 z&2=4<>>+)ABVw5a_CY{Kmxr{pP?xi)2e|7p*GkLImAh$r$s~*81kF43;2PWlROGnn z!G?H=qRDCXV52>~lpB|gc6lk@iAH-(sWq$!^EDCunD;&E(LC;LG-Y+vMyzUJkAxh} zHg|XrdEF&LH59B72ag8@ry45OIF`SL-%w$9o@Wj3aiXpJhfeFL1MOkIeaSqm8^3+y zJnr`R?MLLR+J0-EHUIoZ?I#zMH|DlA-&mlrSBBic+E`{9N9B0_w0EteZFQ#`%j`uL z0kzDozR1cbrqY96yjav~m2Za(8|y#wgs8K*O2hZk)#h69CX2;E^krqO@UQpC4{iua zHSo=~_Kz1?eU_EQ2o+_-k5salv_-8IW~hZsaP>%i$OEGu4di)OaN|t=!2VI@WlVUT zvBpT{n2Zy0vT5*WAlb9RaboF8_O0`+*o42rxsW}yJwsZ9pTZ^(Vt0M?9Uch{o|8%)zh~&(gbR#w9!so$kAKU{yOKF zQj;m^hn7C$* z21dmRi>yAL%)|EbMb>$-jtf_J<$Q?JJ#Oz@WR39fePR);<~#aQwh(;hUuxy_Ee@y! zj%hNKC=VIQU?nOGl>7l>=nnh-ORbb4LwA_QCdDo3ZR)f~nuUsW_9ad>AUd@B=cV{{ zhBn)s7h8$3*f@T5G0r~^Do*|c_CY&;F;0o0hwTN6tzp5ThjFC{fdVVlLbFC4@~DL! zAa*F7?UaVDei_41O?YrU=BR~y(uRQyTVn=R8>8ezcJ*f=r&1d;C}lfXVP-Ay-g?VM zz}55EGO-YD!&oHq)FQL2N-g?r-U7*6@Y}pgBrkOF*KpdFN}jv58MaFpZ^&R6_pmDa zmL*o)s48fpA+^}tA-8xJqgXb1^j&!kVcr`1-6d8`59-{& zVZ{!Gd;c;POZn4n?XyeJ=>>N0%dCMj=4}f2*QJCHNl)X3zTOYc+f?jdXRN^F!;|8& za$c=V!-_g3L}YzyF)qr^4FA33fD$FB4XTT>?ah~2DKpVwa=V;;(bZZ*>aH&$w_V67 zLp|z0&>F0VwA@^{lnK4`a0lnBZaTQY?shqjwgP+D4Y(}(5CyY5jvx-F6=BTQ#kc4$)RG~vY4X|l#`BArQhDyG<8 zx|DqwO3u%5eh$ZS-euL?5b)P{qwr3)Wl$X)x~C_)YMr*-^PH_`98LtsXjI)xhs%xO zr{9nc1%CHRFns*PML5LmD4`?qy6ip?#YI}-j&W^76<|->b zb_pQi@BgFgd*I?KuKn-b9boTelWYQnP1q!xurZrtlWY=>dyu~_g!?))Y{rPJ!?7d`Ct#{1N!m=S@?tF7tuDTUc8{D1Rz3Nsg%Ma}l?pPQ- zVaL9KF%qbe58aAQRH5uv&U0`M77~S2ieNdKDz8#vf}805+m%t?M6+;CArKbWI*Bgd zuEh9&345a8D=~qFVg)cD7NNdHQAKPCsd8VnwQTXX8ybstqdGfvP1TyOzS;HGEB z|1Wr+nNNfVp*TFx%>O5NDE9w`=b3p8Px>kFoa~n$yA5iblT-5GZD8{y^7w6#L`!7f zZ8&@_kuND{(IIQqwqcU28CQL+iLfDr{SEDD4D!a%*4HY_4`IvH$}Ok$b;ny~Ts+%L zjjeORMQb5)(Hy(nUL@Auv$KiqA>_12-dKCjVhS9>{u3^tKCRFDkp@rZ$gdp2Jg+^);~o_{;p1>&#hiHCw!n2 z5luLvjB?dr8fpusf03jyKTP;O47v{<*Ypl#Ga)ptl zpFI^>t!DC<-CxOEhqXww~1#8^BBssO5oDNdrnhKwb#4!%kE9gJ>m)OBGJSCM-*= za&~UvV7bBy{JAbVE!r94+SIbOj)$H4Oam1x zmiub0j7x*V9WGkFuI$w?a|B6#xG>MiiRI|mXbldx&`k|i4ro8us*kbF;HdmwtrgxG z9K~T5&At2r?9@dONjJ!do(AVabdW;c)zP@OHi%ns+~%?>8N3T>=vtX~m$f{&tTBDb zQ6pz94u8*DdDPfN)*5`p2^B1B_I_M0xZ65gK7E(9v|!B?_#22DDQoa*lsd#QvqA5e z`9xvMK#L%7_QKQ<8Q2Sbhnkw^vPPMDHI$;s{k@WbunjG&=zLmY2$h z?za4{><-y=w-wIG?ttV;cG5JO2rNpXL3GW*ZuMG_((K%oIx8<652Xz4<|GJ21d zwJ+3L>>xGNyZ1jE%+jWYislIeJAo20oz#$wLc~$bwXl0d*YvbD*AB?`dthm@c8KQb zrD8(N&^b>snX0eNt0SFV%#UG-`E%obx2FNxKV5(jg^5U|+kg&kFj0xjfSoo74hCF7 zD!~6uu1=TCt;0Qu(!T{G(`K zg-Cu~q$8P?*xc-fA(Hv4L|#7drz9aJM7wH@vkIqL^KjqsNy3?P$CR<jP_B*gu1b3~$JsE#iet_@Y5<|koi&pJtO_Hzrk8-N3fryW5e`-+aL1x@< zEpkQXBh0svV^ek094W~zgDIJFQHINIM%`?pf+rS4RF)mF z7STZyO&@PMW<#)-F3>PxZSHEPp<-^W{X9II`!HeXHVwetJlG%{t>(|{b^NI^pRnc6WM(EcfHaixDOeax+6+yui&Bc_} z`71XvLF$Pct&&@hTAB4Y6Cgb=V2HAp5g)BF+(%)t?*yn0qU30slJyAUiZrrM;|4cZ z6i|ai5}PJyG&LL89HQ~KgU92Jgz=anS^9D@zHVT1J-VOQyWcKy9rUq*3CL(S9C{t<`2($!{ zT)RyO0|RvcTVo8~fsaIADu3FD=X$(ytPxj$1HynBRkYJJbjNzZ+*U%`o0~-iYK7{J zMrzv_jt|tJZOaLgQh-NiTOM;NnpE37m|>W1JYBYVS-r#YKE%^Va3G!Lj;b*z%+k!5 zU*$nA>OfdN^eHS>5eUAJyjAwnIGfp4>fy=EG_MIgv`L;>}@yr>wRXevP z%xyI>-ny-yg=|%QKS~Cjwgx!?6iEFVL`v96!P<5}12l|yhc#}4VCZ0K9xRjaK!vw^ zWAX4ZZ?)lNV&r( zT)Q;{AXqs|Rc?i8Fj)>L!rlhvcB!!vMX1cS4$Y+euA?*?)ljy|6Q8j%*KKQyqrH=t zDXMN;CvX+-_O@>MOJqK{y@_m!6t!({f#Mz$j;LpQe;J)@vA07B}H0`1T>g-R#pV3<7AID+6L_iaCDwryX48 zc;l1wjO3N>^9fXLz{rDo)(2B-PheUIV$*6#S zKu}H@%bu5sHZd9p9`YU0v-(jktPQjR$<7Dph^t@z`T^?#SI(%+I%cg8?=3S)kH%kbioyY&nMg&gnh!>&L9kuG~KP`(xIn&_qT* zYu)AA(J#O8S!=cL%oawqrpw$aIsRG8@2?idENxd>-Ho&()2U}vOK-Ck#U+iM&DN(} zJ4WPSGxkGgcFCD$E1TSm2d$gPJ@TM+cYQI=FlZYYuI6K3#n8{WCfvg z{L(|#+A`7OqJ0$I;W5vq0O}@lEd=m(4+0jgcxQ_?zIBC4I9Ln)1x2^>^#&%ZvuB~i zA<*eZC;EAXGW$3*b=XoI$J-XLXgqGMht-a|nRKnr9&cwrK6Ts*E8?kt25 zuZ^!oahuz*!`Ahl(=V@W!DBIn@^A~ZjD_;C7MySw%Apo3d)YZ%phWcdoPM?V=+WO< zD#Z!BXMmb%MIOYNmYVF`9)>Rn1d1S@fl@5w^fJSJq91PwtFG*r14Jwj)QC3USDlw~ z82dT>N>ZqCX5$VL+Fgg!N$fl9Oyyk>IwQZHry}_^iIBHKF_B&QerFaiIUtM@Ogsrm zP8dl{2$TzN6!AFFNCAB-4Vn6|m0ch1WvW0D5kkIDgpeOrgh2AsE+9!+n3}r=U_i(g zXl8!6*`#d@8GUoWQJ&;ifLSVt@_Qj5YMsitIf|T+du2W!tT58b%#|Pjx+Xn$-soP; zdj@@Fse>&&n!6kHrf@2$Ed`v1ak{&qD}|8pV<^LzWOp}pr0`aTDQkC|v`%8u?w*yK zPg)0EyXWO&C#}!nwogv0^$?CNe$r~)1Sw|ABi6Uzy!wdMP`?{Ystg>lncje)^+VMJ z=Cle=jf+ZY!r>oKxF-x7eb?cj*ogB32uUilj$OmCcvb3@*pS$Wh3RV5%ROO~#%EdL zIpiykT4w@?kYcVh>voT8B^zEg1LXsK1wLBCG|d%Y-;LI<6w`Qd34Leu6?6wtfrh7` zmn}Z87L%@7I@{L(>F8@z^fkI*Z;^SR-sxWfy6tlvUL$Fe-$RG7>^DmZ%ew zyj+0eb;r&fp#=qZ__H9b>ssa_#2k5MoI4kBd(PF2_1w9jUdcFZcTp&whJa2}I9+G% z&ZX|$!E^Ht%uOzHewdJoADXB6Ga`T3hQn|nUum&}te+i~eO zPhQ_{oe#zDlkK>pQy^b&hZ*3qX}S7w+&e0jXFhK2tIwP<90c$+H-ue#loVwoqzST+ zMcD1ZOm_lKL4y+g9z!2esC#+{^NI|fmYBw6DGeG$`0GziT&Q~MR15<^)%$a>f@;5$ z=1J9QyTJjq-$_f=^|(Fjq5bsSfs{7Q%7q=) zG8f$S9ai?DPy-#&Bw>V9;#D0`@?`YNJ3Anh!T!4g#Emt)1M)|Se5(Uz!=-Zh7h%Cx zA@_a}k(Kfea%$w`Uqo819Qq<2XI(m!%lr$tSvuRvSA~dXGVn$MjLwKe;B|pjIDV2u zbPgg%$4{({qoF~9evk{!8?G~)f*h?fBN~*U{Pi5q`Yr%&EjxZh^O#Wd z*?N3bZXR%ozEF?1isk_!@AYbI$h$A@g}6Y3vlLsAdMp-17x43CEA2-YREvnlv~v82 zRxKj>e5Xa!@=0Acytl@^H^;q@iTHxmEZWXm9-z@9$yP2Lwj5cWP7(X?DA>#LE*Fzn~hUFX+e_Q_yicQ`Os-8$A;A& zkpZFl-yD#~yI{5l_uNiw5#Ah=w{=<@>ffBwhC6g_nmg&73mR{==JKHeT6n7irv|Fs zxB4Nss}|mhlA4MeBd^~Qj_>?_ue zP3Se-fKT#eJ>&)8e{CQX;%%e>tr}PJ<*$?SJ72M~R{RwTr4+n-BvMEx6&WX`8m;V9 za#Fti6)UZtvS7vHL|(Ua@^nnyv|ofE;Hnc*{ms=Ozn{e35Uyim z`#>wV@n$~sD&V0`K<(&hnKY)Gp~FT}o)e-%j$fk#P(EBhin4?3Kc#)jAId3jZ5Sn!2g_`>KUYal2tG>0mw`*4`UCi2kYT6rNd6B z`}JN~|5ZF53HRx*f*@WWlrMi3JG|G&<$^9eNmWCylQ92 zI3R!aHDCws-@j(9KK(_EK`v;j7}h$MiUE3@ib;#`@*)um@RA2@3!lsBnrG$5ziusC z23a``Tff4q*p`~xrBc6+%6q?##pBnF@~dBmUf|b*@;6_HNi1C7H>~ZhUk}Oiz5%1P zUysQ9zJayi*OT(GZ&<~kjW@o5y=bLO$0^#9YT7E{r#GR-3%#0W{w7j|7T6M+? z{Ysj`K)f*OY^#kI27Q2(q)Df!@+t|>cwt=rq}#f|1vm1fwcGW=l&pRdvP+q4deRE4 z|IGyY>-JpqgzyL4o(rI5J{NENc84(lsZH;{XNh{0sLc4tZN$YZQ_dI27zy5`8 zr41;>)Mvnfj8U(O^I$&fX*pV0Yu#s%mHX+CCYY2)TXBV7dk4bwa4iwouV=!Tg)Njv z{ZQXcb#nYEoWa*g*Eg-ur8l)}(xR(xVLsr5K^&iA7&pxsp2K){z|#QxL|!gViQ$Hq zOhw(tI-nGbBYMe{ya!d|E?dVptstBqDd&}MLNqRxNj;dq#d1pz>?>=*(T2?A{U=4q zNg+ne3C9m`0smbQXO8hKyAuS-{Go#=t;6g&g-xJulp!Cdu=!98TqasohD75!%5VWF z?gBFXDC@acoAxm)x^tM`tmby_WP0FHyK|zFRN#oG`X`9PHVctb;jYnU$1%j-IV*!t zTdUG^J#o?ajuLsv)97=FtW(Z^J#A%gEy1$@kby|7zM}`41+E8e+%Z7+lP?!_6MQJb z)o`?Y?`dmACM8#*bCgUQV}|RF0lE3VK~Hx~%a8xJb^3Z$HTvPhd z0&WC}u47S$d=L9++jI;%&wOGuG^&RKI7T9_#IPR*>%=IFMi*qj7#0qf&{9_-ym(8~ z!OY=O8ve_~kR1J%wb*sny!^|ztn4L0#rQZE_3G}B4D?#7@fzDXy|`2omRIyz8!O7y zfj;)fxrg3ZL|K}acQ+IPJ-lGt-5tC3cXy9-ifi0m%#Q^zx!gl%GpbSi zk9KP+#EE0DbMWMv zW${}6Fe1-+7FPzw+U2#+qH}ORtKDawwbtc~fxlE?+a;>>x&R1&S5B;%vG}{vbhYRt z;jwJfq^F&J*Cu^^gr+umR-aV>VZOc(cgRZRSNi~MseG;vxcd2+oawXDODbpu!Af7m zT}Iu*Rk@h2Oo-&wxAul}Bk;0h`13Kx*v9zzc%o!TV~OUcdSvN$u(-fI@f`>SQv>oR z-@$S>H6h>n4vt0YV(aW{e1?cpv@dxL>VYT-Hj2Cahyf(8<>nEAI9`&)bSzG194{1M zj+bUftSY>4DpJUbja2r=Yx$(IHw5^_LdwNIH2Q^6dGfnfp6d(q@>k!5#2l4xeHRb3 zSIbr3v#!yxX?{lZ6Qx*}f+Tg(4`rVym0$m!wfyQ*6683VBQfnP}JOSt;_`{@Z)h?;m?M#HP_wY zhsq_Mzk_qsFLx;nZ(0((c7e7hg!u$++4 zaF@*bJ}hDaGWYvdw7x(cpyxw*#)Cp&JYJ@4s;AZCzZjLryV{+bb3iFs=spg$TTz`XDqMQSDXg?L?8S?mifVO!tmhkvIhTmO(VjT{GoUw&Q!O6>G&hYxF~l56#Pt zA3_X;`->k!#RWHY08b6T-7^5)R!H7AfY%&~WzPU^s}{?*24LG*EYJ86E|M0@%YK9% z#qnnO=^x?d>+yE^^pC89EiJr&UMCvqS<&l6A72+zct#Qpw^FQe`^UX9^kYbD#qwi6 zww9k&48;a(W{s9Q4|_RROC9auZXm1E8=Pe5?Ln9{bJ2~K2Kj{_TbYYn8UrXr_pMZP zK>p8#Mx* zs)w766Xo<+GjrY(g}A_MFv?Y|#_w@J?p+4;o2aNIC7gmdmMHK02~KAy57=u(Eh+}R zI-NRRC%^j>YvtDC4J1W;Qk2&?T9}qm!*!xaX=LE1RYw1Si^ZeT8pIj{ce{449)yUN zBR@09U$}U75X#X!`RX8^rpS}Np91|Jx#OqS`Zblx%!IFd9g0A>!T6Px$JE}&p@6*q zr&c;W4$3FBbk=^z&!#}k>JLR&oFI%H%8l(x59R4yDXk@ku&PnDI43^jR|~|UN|jEf zYGbdkkrxA@mW@M=i7noo*kn_@$<}z2(7GtavT>+S@Z!L;i2~YafXHTX6=5C^9aKUp zge=YDx45d3o?+7rU~!ea|9`AaS&KWUayo55Db9dNO&wAh(&Zoj$6DrJ+@V%o%GDVm z3grTwa^e58vaU?)qSwd{iV}XQF8&M@#oq%k?r~nQV>l42;6P>YFMI$dc*i`++1d(E zF*Y8mG~6xue=xJxC7AX6hY~zQX_JgE00!98Fjd)dJ(_aVNd16r7BdUPOM;|1;{xeL zF;u3*&`&XZ8W)sL@8q{k4vH3f!B=JvSu64>E*+i0Te}HJMQp4y@c-w|P>mC^VaQr~ zrOwY?`49OOWD_ySGK6D-Xk-*dG4$Mx40^wXtzigWhPc^~GeejRhFtzLb_$ep3prug z_A@ZM9QoYO!0d9Q^_;aA8}BQg!}3`m?|2Rx)&lv(=a8jNzVMv2&DGo_Q+{q0`5zk9 zI;NnuOW?L149J^)jwcTSVZOPGVSKRJv27u*GRPsV>VwdBI`bKRJ)eP&2irgji|n*% z&!fli?ku!@OVc2wk`SzU`hzXP7<9Q0RX>4)t!eh4r&MRxoyY~2bq%<@h|P?LJj~x5 zeE24a0uG7*T@?_GX5s^M+zHB%jp59ui$gV>tTvr$*W{a!*A26v{pn$Fl_CjeRpJ73 zyGOn>4BUF8=Xq;w1(*u72AAUcB)0s{d82WA2jTBJG7_L!%PjZy>Z|xEq}%5lK^FM9 z{UhbMr?*FQadPAI==P{Q@jR=<`<}P5ue%*H62_^-l5WSoht>@8^1g?Ppi&=NXD*QM zexOP-0bfTx(JRk)?dP_9T{*b?XF6I3D%~Z|`Gs}Hh2_Mgae*nZl5(zu73V&1B+Wm3 zDw=6nMKkOQn5J3zgI_=-hx__3z{TOF{}KvaxOu;{Rs_<80V%CijJT)CE>RzGQ#X*~ z@Gq?``Czd90ymqd>G|^5)#e8(g)so*RWT*nl4$2eqWJ^0Vcrxf`InLLK&_noCD!;_ z85yz8bv@7^Zym9=u6Uqb7)=6SMCuija15e8`l)sS@v3^3`5x$^eMwB{dH|eFxDWYY z5Jmf758fQ`5$@3Tv2Ya&KIFy5f}~om?pPj;Myc?Y^7VB}E-bPeaNi&&g!{ox>27)h zi^uX9jIXgLtfz_eKtp^Y!B6Wc_4C+#9qsG1*IfgX8V-C`)Bpx1j?cpEtV~t-S)Zmj zV(*^?#ihAwydn*6r7(=l^(oqueAuvN@W`HrwrTSBzp|Fp1Izr7qrR)AQM4b?>ULWK zOGIRhCBnbx0b?uk_w-iRnrvW+X-{Wz>H$08>Z?&teEryu=rCw6T)ix@eq4y*bqomD z-zMMuwUxPl|2QHKc&m@7ofQ-l6d+o9dB|i9mS>6e0kBR z^*Pr>qnsMGif}A*`fscYT`xDthTnj{!|nYICgaPEa^g4E`V~eGy>^SSoG~WJPZo3< zfs+w=+HawG1zi+D#uY_)l%Q11xf->}DZ?;XK)bIipjE=r2ib`@g^Oj~Z}AA?Mfl6A4JB z36anVB9asZ36Xdb0FlrsIFa()7p;uTCnuEZAuZkfgeU>S{saI;r~h0PQxN=_k5709 z|L1(Rmtm~5ZiGb4({gfvNCL;6%nShAUqbT97jZQ(B!Bgy6@=vZ4npo>#jDi}?M?XPkH)M(whtfvpo)pACV=pA=%@3MWPO2||3doT^;4QNdo2*|3RqC{+J)Pe1=(x3XLykw? zLr3{PjVW=S$j_HvNWCLG;|KgN>X7;4;Bkw3W!1P92rrsdQngA9(!{~LwfaaK<#2{Z zrGm0&-129J(FHsKj3XiExn?9jEaab%h#kS)xV7R6u=)__E3_Ru`0aFvSV{AXe#m=M z8FBi9BGb z{V5n}UUaT&aqvuVU)QEtBu-l#h^`x?ED8VeLfC`wNJY)1-3%y|;KKNJ!$uIU8%(cLH6iaXZFd61KIYEns$&E?_ zUs&e3J1zF;v8ri8?s)}oYFs-j559tfM!3gcv9@pKR<9QmG&iqT3f*g=U(r_(j89Yr zl|;`d3(1*R@Iq6av|q)Js!pzZ)!LR@2XPg5N5pV4Y#DjQR?= zo~NrKdfRhDwLI}ECPA$p1*pJ9al%mC&?$d}bk_~D@^`OVtCrQpa9`1=6!hG!iY~c$ z(#lv?(S?ud;9W#9?H)=36eLz-kdeG=5}2>(msd^#^A)4=fk|ui1r?L{UYZ-&?r}FK z#nJsquP_?iSihUx5Rwl#xc#ILk_jCqHM%F%1CCtzCnNIhNqmI~C>({ZRgAm()Fm>N zIw@EE8H4}HDVhIg*po%%y??d}v(R!3x!0HBNYiQL2EUy6GtP@|Xpr-Nwz5vUp#e5b z;Pk060je_4N%vHl>R`--?o|fl&i}PetEU8od5X;E2b3;_q>p>8gm4h-!5R=tY<)Y3 zG28-S8H}+&Z6;RZ+ioaaN$A&>i z;{!>Q-B|z`O_gPG)nBa5^)*;Lu{uDqYDl2}%0?W)Lej!>(KBu>a8Waz7(V*EYmaNh zO;SI86y1l%acP{|UHu7PHK-iH@L{Up6tB~*XN<}}p((V@l*50)gshft{lz+?{>CPf zZEqDd;1{>52@gPM*m&|+cBZ>MWr+bO>sen>+2_X!=y>&qNVusRv4RHONAqEf9TrC$ z$o`y`-WwaCL?Byg4m6Ra60X=&!T{M->wX>d(1SeJiNdOst$Xh?0=SX^kr+Sp`5-Wa zOD;XQGzF~!zUS!|hY;WeLMKBILo`NOQ3Bo42lVj(px2cxoJXv=5z7UcEpcZT2~YCk znTCc!-Cu+?e@E?SCg`9lj0@VS=$5}PO zERYIGjuD)#vPzJ1Jy$2sd7aM7_$C7@sh)!$SG*iQK1$#7Bj;%PIUJ^uG=M(9OW7Ph zE)>m_JHh^_uooH{wa+R0dA^FmZ7u{PQEbJcrD`jN9^2e&bb28kpM2d4h8GS&kHWUS zFA99L?>c;wL5Ctaae7$TAWwV43Z^b>z;eauV{+dc)@l1yM5A#A_O$+>O_t_kttLWf z2e2;$>=49xTo*QblRFG|*(8Zllt#}9`~K=+k^lo-BfavuH*mTzG9~BUumTysYWD#V z_{9xzp2zpBr=gZElUGdR<+bN~W$m=JY31{nTW7dW{L&D;u3>%{7ccbdT7S_ge===_ zR{WxiMA{P@@Wm)r@lZ1T(C{xqQv4OyA8O^czhaY7%HzaVkdF&?*Hl;E){4+e@WM2M)8knoEdexhpcgiiY9RG6)9(hr@U^iywJBx!7C>i)Zt}Dp~lZ^_1(u89Dc+^|76P z=zpAb+mzUEVoxTriomL=8B^lH6lE=AN_=r4`ELDMKd0r%`nUMgOsi+CH8+wJhz4Xn zEI&|33M`$thlm+;;akf5%s+KmMd+FmN;P5QC7R4ZmpJhb@Da-z+tTa$C7KHt73*8? zsIM)+syM2;EeA)-j6buj7T3H9%aDJ~Hs~x#P$#d)*;J_EHCqPcBQsWZ#Q@ohl!(eh zFgM{34CsnZW@o{Dg^TH57Ik_7fd4 z@V1qe4N~hS?$it81P8q*@Y;^E{+cHsBD@WgjDC6eZ5)c7n2}$38(+TyQSrvp2hAJ4 zxJTccM%xejeeT2H4o(0aRo>=dT4DII$HUFN+!7(+;nraC;e>H~xUJDlu5}_9(1!sb zsiD%ak#QDk^Wk;9kgjM+>&Mz94t<{%4p6rF@C>%cH=|K5d$L2``wn35kZtc+ITv+6 z#zI#~lvVg|#U&Xf&zh}xC!45?%eDH|lliz)r?!h`Yt+j-l~|5ax$JLN*2Ypa7cW*0 zexG~?gO96t$;X_I0I=2ydEjqW1`L`H{|!pq2HE~MFo#|_{5O0$c|tCjwMzU$v>2W8 zgQCuS#3%2X#hZzBdsd@XqJygn%XmW2P*CqRiT6 z@;!V>tAu(%Fc~vd0;W-iaLng*A5zyF+>9Q`hi&=v@Gs?8ZAIJxOGKfS;~AU-vj< zO82|gl7rYB@r+3au?O+=MobKS2XYL(N1e+IOHC3(MV4@+LrTWqggW?%b`u#eE@q?6 zBnl@Xg<3|ZW}~wHU2B!EK&iB__>oFywn#qpE>4Fq(KHn0{HR?yN^!Bj0~g?XNKR@t z&V^2j7ou6bwX8+4*;e}CjI$2ScKVf7GNWgi0AUU|Bm`d9V4}yD~d&{X<)q5aDZD5l-k}Golct^vwu=-A z!c$Y54ia?p6qyeqF8;STNTkG_67%f_K#1HAjzrCmL}{)$e$*NBV!G=bl$n46bub8W z`~&E<{~^r}t>rk_p}1y3z`xDDw~G`4=B(CKRI-2(kjEn8lWOHO{!s=VqWiD?)DY_Y z2foDf9(L6KXqDf2563(UA&G99+57`D`9F0Ri#i!-PuegiD$(7o<{RGJUGCPq zu;l$06ba>gX|P1}n{)JaI~pP(u3m=V*vs>^=>z*f$=@v z2HMFtx!OOHD!0$$n-A}H%gg7j9M`*3^1*os2?&3G-dYzf&2yg^2OZJ)&Xo!durxSV z#*@(PraALZkMzE8ty=3tXTcG+LP!_j9N;l1_D`JH7sR;lyLqzsedzJuX0Y&Gq(yB@ zFd7Saz*}QHSM~6pO;99~jL_yHQ2|_xyY$g$!Ure3CuINofc~F&!v1|2=H*NK1H5td z&ne)HR)L;&taKjhu)%(MSJtNkbW6||h^Ht{{?92W$ep2l4KA&S;Vj&GMg{1Dd!7PQOR4{@btLB}IeJI587dE|QgGID-k?WrQaKZ7)y~;V_vU3o6)@5%h_0FOX z&2?L*V^IM2%JRtMj_R5^%0niB+ z0dG+En@inRMN2Bab(aO5aaNGE!YHcK+jm;%6hQp|^EV6S0bvJm7q3Csp&f<7C<1szSD~e= z8nvZ8m-}WHm1Az`9SKp#`G+dH>*Vi+9dKRK&+bLX11eyP`5`&^HR0ysr-U z9I7YprAF!kQ0>(LgG|hMDM2(*M9hzi$=bijt}#iGz#B>@9aYc|tzO;qsAVsAUEL%D zmc1!*H9k^|TbG~2EzDR3(tTV?p8-%@0r50{QR1*=Z=;f5v+OK->4Og2@D5WAy~7l7 z0KUdcTU+kNHN~`-!5mfR7}sFwAo&ju2T_p#9gp)kUW0F~#Eu$LC+HX)Pl`G>`;Z6C zQ#9ZB!-K>G>1rpEzFWq5V<#OdzmOHSy$i*!Q5<-jspqN#bt0dK`iKy3Z@Sz4kKKz8U%dkdxeLuYD%D>lWArTL5_k9g0+) ziVDwvjJDe4V+-u%XP_OPTtyh%qEb4df^iYcpM+iu$*fG%hJPC5+<<&zfsOCQg8_lK zbNyPVB>rsqpwuK6`bJ=bqoRg-^2OoKUa#D}5CiO$2N!}_c;#mo+VnZxrxx1uxwNr` zb_mY<3+=V%0JYGC3124ExxptyCf!R>>`DUicag6<%{f0S8lhw zYmptO&+VllhwTwta)m!yQ$IC@kB(-M`sr!ddL$!G8gz~zrq}Y#)y>CvqD* zqxh)(+pJ=D!o-I8rxK#C(pMT|exo9c+P%6Vq?G+yf7Wol& z=zO3dM^?SGQBKTxUU3TJ*h2K420ar$8ZBFv+8NPF$u2TA3_=sCt7k@aJ>s+Is<@!?7Tv38`;9Xre+y-a%5^t8#=D#qJUD|VJJU7J?%<<56t3L>^f*{i~4=;L!N{HUs&Q4d}1@6 zf&itc`U7fdcYCa4;@fT_tKJV?>YYFZM>26+Ut!>)sLh_A)HhaJ|cc#Y(wl zIdEDjKepTsWENw@l{Q2RL{wy7yS#U~o#lf2*m8S)*}gF?K;VTuk?M00h_?2PVMSC@ z+P+DVw0OFqTyL!qj5D4~3N&3M7(p^{mlViVS>Q+oawjsOX^QJa;*!}}4M$niN-f(OF$G%|q|Z;+yKt|0sh+Qi%}xl=T-q~WN_m-(ygbU%qo8ZnLo;BOAgs#W&6*vmY% z%HFZo6IzOvvQ7SPYiJF9$Hw(!L*n(mn`@;NwCP3jwLyCoob%wU#H@|LbRd%BdZ>V^ zXK9W+XycZMJQB3804E=Zcfq`dj89R-_(Ec=jlon|dhz?2*W=IVH8h zY%x!XXu*?fi3O-xYWLQ3@B#h6j;L%iows%fHcB25Msez)(-Imk?>MDqu88wi8#1uQ zrkj&RYaoVJ%RAP90aZ84FRZcs7gS@lAek4_*unk&I(wt5sYAZL z4gyOP&fzN5^l0klQ9yw5_YroWI&{b{9-RvGGgb0}gN7d)sv*U3K_8))QPFgh}`0jC?Q*b`1Bau)Q(^Ofwb2S!x+`2CnI;bop}FUg~KK>%)lv~bn%(PRyxiN*l)`w;R#ivr(-_nGyvJ@)$;pdhYb zG@2tx<|lN6#`|4FRac9Fqy`@>0?+~~X!7cv52O6UX^-m&cBBrBMkQU9z%_(CcTM;S zkvCv76`(&x@2mw2XT{U-bS)Y@56FU{%P@aT9hIBL)PO}-#_$8y3=lOA39rB>?&o5! zotmQ4(w3B*3DS3X3udO-C@Ne3e zAEd^D!k9s-j=O^5=*kR=m@~NJ!r*XQK&E2uct1b{@fwr5S%d0a<)|+C(v#w{lLFrk z?ibo`oUZ%gE~28BCUMU8YG!!}m6qpy+N+3&8 zu7U&)mR5<%YV?a%M|?G*!*taIc{Z3ywE!di16oLp5YV zvQ`3)X^vy@0vy!$LS;v*y!g~0*^1?ywhQz~BTwl*Cvqn#+;i5eg^W^CkYdsY8JW+0 zP}WSsl0M4*pjB<&U=V<_&1;l|@IjH`I-;lXhjkU^wQ6M{Z_LR2L(p!8eoPbJ9ho+r zhc&s84@XE+q?dj`<{xwmW5i5`;o#r#iHaeZ*=m16hFf)xe^~Jx@F(POs(34&;qPyK zBm<1zse1I|l%_G4I65|$KIoUf+6GlB+}F3+S*tz(^8j&|i2*vp#qleTKK)1F56jSY zd%Fwn729#_@WH5jaJ!99rT9|tlI4joA6Q^Aei$vnlv3RDge9(BdX zlxa1&{n69ym9FGo89W`jkxE%`y1n`m59xmQ3tR@Lb7ZzuN|}qFHlr#}TLYOFXl~=d zY+pnd_ZC$vSC`sK5yLrIb`FMoSF@D4Y12bv4YEj#SYXi0Bd;KmuCz(DV zf?KQUMiO-XSZX2X1<0S;g4IIg7m|Nb7)vnpdgS+kbs~Qb`4@w&fG-R7SCQY#@k{c6 zAyEc`;4A%zPtRljG*o`V7iIq()f0-;Qvg@rW0^7va{Q*4T9S;WaO?Z^jN#tLOE&*6n;*SS$pEe#)T~Yabj1uJ!alK22xV%qbr%u&z zP#0H8j0qL*^~Lk}05n6B37~zjIfg2lQYl!EO$ld(&cj+FgKr) z=mW$6HX?Evu{y{-QGQjY^817FBIQjc2Sy38UchppU*Kv+UO_z8?-x-X#$^+n$%~T| zPQ~bgB*77I_el$YJ!YDJ8l} zk5bYaz(j;30KOWEY^F59&2R7%WR(L9{>Cr`yo~jY=rUoAs)%^W@A0x9Rbv=I+UZL; zT$7O2CB=pIlxtI`eDgva!O-`-hg@`1!$nz7A18v$D-yM>F45r{P>~Zn+?v#QQ-+>U}Vwhi020GG`M?Yr!>*4WW z7}0%>@28-T=o-aExbWX1dHm^YDKT3fzO@Gx2xUGci8G&FPMJM<5N9X@of;v?93;=+ zK}zRYotLLj1r$)eyfO;Xd`o_0D`SZPfjq}Qj>>xd=l|VKnz_g*KDC>Y}+*G|E30~Gi&3Jh{FGqJU!oViO ze0d)yG1f}kP66(UDoTO|wGqOWm@^wTau==yuRyo3zElvZ>A;`D8q_OM2X$JuTyCe! z`HSqW`^tueQ6!4XDR&towuou1j5{A;GQ2vSi}CPZT}n(E_fKltd76>(m`F(ToAMHQ z-^Dh4(H71!`TdLS#ZWf>@?z*)tK}OP+bedI&vOBT2tCm2(SuVwb^S2mR5Ap4L#h?8;2y1k2`W&m#p4vXKbh#XCRsD-2lA#M8$|p zjJf)S7!>nKqA|IF>$;&&eseDy@r>-X{Q@*&Nb-RW2sd25vYq|TYFf!7r1whOvNPA_X@n*_mloEwa2A+`m@GMsg?Mm57+!>!d+Lpkmv z4VVo)nzwPEfLuj5w_|V}V!s`D!;E9zR#U;F3=RvP%`tZX0fP8QMnR7BvwYa=A3;Xuz~7g3^_8liw?9*?)v-fed@ahp0)lU>pTW!k<7@joqhI(Kt;R6xaI{ zw+cociPGVWVm_Z5xHxR2PV2h=-OQ=phiOEcxLbg zcc%0f#`Psi&}gkCV1k3Dn0Ge_%G!#q0oD-o8uQ%zBV`;>hp$@nxI~|;+bz11x_Kf# zIzTv4GVvp0vTnb9+C?BF3_=}?JEGm}G)1@BNAx|&XpwnT8!Qjn^dyqSREMIrHXkLT z`_V!IrL9rEwcpOH2f`p^F~xn3anXuuDZ^tbKh}JngY8TtilQDzMgyxoWqTZ8qS|zv z@>B2&iXH;&AB|_Dry9Y(LljRhFFOA9U#z0?s@^Zwv7acF@_ng=f__Hqmxkn1B{1mv z66%DE7#1z=Hb&`}#%NT*J=!&nztTrDIlqNF@s$}$t!XiVl1YyFuUXX|t+CNrQ*^nz zc@$5H=N^A`ionFoQhSlET$~=si!ZeUS9GDz^-&eCznExQ!9*imzYTEOcveAO(~)!rH5JV z(}2J$I>ZFG_;fjS1oL80bR>al6#IUho&>S7vtx?dRMCf+O>;o!FbSV%Ou+ufoQ zPaP55-@<(NKq)ij5>txwM=s%n-Z84)$2{h{W#k`8Y4(U#&lu0T@3jh}&n?%W=%60^19I)|gAPjM_vnr#O8Ya5B|aJ!Cnsa5#SlMWy;7FIXq#6-PfN+yLPA~I%*2_5q= zBf}V`p)pTRaKqsDWgKLR&ZAjvwwBVJJ1XWDy`EOfi9>7u7L8hpMd=K92nLojUW@L7 zRrgsYpG8VdIunXp;qI0kNofoJ2T*+c*vD`c@+E0DMFiCcz<0W zw_S=Il>M2GGT6|h%R9^LRqM)$mIEEn|Awl3d5n9;-1e1R`Ta6* z?N|L2ZKp?B4#L=;{O+81BsZI_?&!- z`BMOw^i5pTnB@jv3sZw$u&^AU9by3GdELiQsEvsY-p1qcMnGPDHQ*_MX(I%$1{Vf& znpd{JA}Rv46~wRpQ{pQLX9HNsq3fRD_TJ+5U|UOj9;fEFhPgf3<`Q==uOJ{O($M>` zw|JcL>L9V>@**C;8{x`k@?kvyF_5y)^vl$1?Cixe{ahE831(+AgK`(5ZaMV^U6Y^y7BE+8r{J~jY~xHk}g&{P!wvW4H^T1AVL_asF+>U zaB4oDzL1?qeKQJ5(5?^-i<=jV=EcK{#l+(I#azjN{8PD|SqcK-BD8G_iYV;CI!(nl zKX+;pW+#$*Y1qxjatI(y@Nl6hB!V?R-yW-ngB|kXYeA_VdK3mv!c3WXponEa^9jZh zW<2Y7%qI$U!SoroXcibm)b_0cFW7I!;$3s-SWtF_LhLzMZgx&G{h%d2&Q2-`hpP7aGt-;&MX6Fv93lb z$^@9@eA>bxq(Cvoef}VT(+am3b9V~SjX5X!A-^D#@j)H6?w@o4u0b~{A9aIwO)`WZ z)XDq`Tt(n4?RqoEihow{Q*l<;LkHi!SadFKPZu5OJ@_wUD8orCU)ixdrmnKDPz-eD?KQ3$+{2-i$YnWvK82t@C8Mx?h){7|C}!YW$Qa42BRL$s@#&OfQGJ zbcht+UJ)$Dx~t|uJ;m_whe8Z*uP_RUD>KbzppQr_rj(36B23k*3~(`UDIT{kGcO>? ziBa)o&K3(U?W9tL7@Z*{J(^2r354`fQ;dU`Z^s(Si?^9M#E1epG;`bj91aurHT`8g zSy=w^SIX~SZ*SPjpz%J#^s_&QRu|LXLJjgZkNwm)j_;QK8(`JzM>CD4XjnM3NQ^9+ zS|nx`O{9sbv?i8INFVJHGm9sei0LIth*MGhOA?}jK4H*%3#@f8gA*EqUZ|;QZ8TR` zP%WNZ@-HibzJw%&X;U<(4PMN&Ly_qeRG})y5mZ1Wf@CM2FUx8vGQxdY)<}L9aZU1j zL{T->y*w(-N+>ljwb0D~xZ)IXT5~y9-=En+jMmKVz>A|M1egXU2ns?inh+DJ!BXyq zNZykodQ*m&u0m+05h8y|p9+F3h}S8ln7I~2g9@gT=1Eez&|F?F3@qAnfur`Z;hsTI%Edq2nG#EjiY8=P1Hp$#H6i*>PhKjPa>4$A1+HS=pkLufoCCPS^9eRB_2$!sVvB7bqv z&RQ54p(=O*HUp#b-Gewh42;Rlo9yiRz&Pc{^4YbaccU2JI2vJKoY=7qa~n8fLT4L^ zbZw%D?SJr+M%1CEm{49ogk?fqL;3=0^HH1Fx#BL?<#TtL9)hXoe%D#ar6 zo2h)5g;;ZCjx?+7f(4~q56`caW%810$Yf>m{%U)ZZ+9EC5zqq-GL7$X zciY~+y<%|h6i2P{@B$JP^YV(@>?QS}j-Y7R+sTO+_%x~_V5rOhKUP99YVEnGm1f6~ zJ!Xq0e>=~fiv~%=%_|T^Rn*=^fC}S~F;5G*d2ufD2I!=4)>8?cjuOW%My9aHEfOVV zqC&oYo4t4~M#G177mRJd>~M)Ly=O13rkski7q`ja?Ko&Co@eo-i{!acM&~7^w7bGx zq?$$^ell6l6NQzyiq7Hv7&svcXzKye?V=^VSBrxCM`>O|1Cx&)!nKh`x$Ka=enBHs z8YA-)LAm!3)M8Z>RR#t92sAl_sp~3ecR!dqkt(KA=Xq{WluvD*%P4++QCs32B9pLl`5~mp!!8)YEvp_HIgzAhuX1D=3q33X(MCa z*e68|R)9&FUt_O43rN6Dghl#_CdR~#z&lMU%6u?A!Jk9^4wquz2l0STjlE)1lmMd* zDI=r`L8t63erlQ4vr;1kb_GV)&*dvM_IdeL4Vfu;=x90z|9>u+Pyd$>n{XbBc-(Tuy;6R|@(c4>sZd>O%Uz@7Ws;+s91#w>xbAYBCGKBcg_vJZOacA^ZjLN8ub9 zxf9lmxG7nDC#)$v%BdkIYjwKHWKX~i?MbU%?7D|yxB?VT6>{`W`?RI06NrnPt5O4$ zVaD)!rLWdr`3dg`g4Cb}z&i`SKOSB{@wo}%f{%phl2kAuyIMjcDw{RX#NQ12NED9nnp^imG2$$f`N$-HaG9LEPM_{Q=q@PBW z`pOZ=R}mcU&|!@eypLaWm%S>Z3ei-Lj$S+p|CB7g%U)i;q#yn;{&bs5CgIPChtmjk zA|Xt5(4X#C8lf^z_sfZ^L0rymH9YC?1JtxGIP<%CNRLvKir)w$69gs`###fC(S-Pj z{zC=$EN#KvIAYCglQnnSJLRKn?u4|6XsP8}SLqSV8%#6kwk+ zb~lcV!z*69?7Vry$1sIFrqvz@0Q&U<8LAB?gyVHI?BVJ&V5cd{8jkVxp(lQIHfuWD`@512{=Uwb5_Uij>zZ{=mblVTzVc8M5D3$sEMFM zi@ST^Z&t+8Xq>Ckbn!iTvf+rmborhJV^|?oBl=0K3C>(>o{6DZ4UB!jD zNN?3;8^!LUWS6Po#^uj}qj)Q&P>vn7SJrUy0p@pb( z_tEmg#si$s=q4_vQLIjwW16Bb?neB$u5VA%HM>7^Kpt+SyO&L} zrO{q~{oWB|XiF%lD=e-=d~rfN4KIeaxDQSOffY|69KoNS4ErdbU!zyowJ#)npRzNz z6(gxop)fY=y4rnhNE1{PfB(O%^8t^lx)S)CM}T1%$R9#7`GX8FfuO0zN+q_jO=-}e z(WV+}>Si^q_%*fB#zJk7)Cm$GO27z_7cj0UhzLQ0f~0P0qsE%grd_mriuYlR-aQ1b>OE7>j>5_yvT7qs zM4#4ExsvpA0;{r_v|5wbxqV|O#jZT%G;U;WxnaQR*ciGXF0L&3^vJ4881-o-RbtpV zN~(H@nciiyy6;WI%qxlEq(MJvcjMvM(!%>XGXW&hi{*dr53EaE-h8vI)EV5u>bt}l-GYa=_d4ONp}AQ_lH2DO?X&gPI2nCGM4#f zNz2yIC15X@woA+m@-NCi%-hxGjBcfWRyyGask4Mc|20t*CRgW5r}RN`9(MLia)C4WAWbZH!fn{M@3d3c#@6b<3Cgn78Q+L%We3AfJ^7{{96{c(%=zz~ zp-2wTj)MFlsBZ7-aR%FH2RFctwsGb9Of&iVy|wJ@edM!c$eO)1QumQMDxMv5N*-d| zmO3>L(GT3))$$NcILaLXl3eBt2)=N{8GVR096REKw~<_FUQRpZ+mQZvuT!@zbgBJP zud{bs=&O7%cX}JHEp_s@hZfAPLb-QAqMiR*t5dTb&onx_w}&#)-Z(=R>82(8vl7oe z9@rjQ8pz$zPWM(hx$Rt`CplH^j5QuC-P(>NZ+1D|?O0Oi9B&U@D|eGcAI5%u&b{nm zOyUQ7YlZn0;+i)oIzpFbhxQ`&PxDcj>O;d$ zMF*3hOx6C~T%i&3Sx{>S{zzqe!)x{|37bLRXi?+SE1d97jIT$`70CEufVO;359y`8 zbS=hx8r3^-f5u*?ac5}hV%%Y$@ODiz*3Z1+{$R_rO`(m`HzZo`xoX9bGbGxMQ>%ZN zskW<5pOdcIu8uebyNH3^PWdhZWyGo7#hTOawC^JNnw^1Np;hrNippIj;&IEhJF1lv z&jN(S`yx+_3b;`@@tjS#RarV#%6BR!9+m=o^~+@&@|}s{Nqy4|U$-+~3xqX8iE`q{ zghfd_a;cftR|7M9v@K<$#PS`6FEE zT71S){wO^YY<21$4PC^SA6Z?v+7@w#cT}YOxEvLcUaPzhmUh=EAK)ov;t!9XfmcuP zls~}3a7^<*C#Og7kn)SJ6`w8{3N+z$IYNRtCr%jjOZYhKRqzfhF%VJtZ+J|itoGvNjF>|`49RDlFLK3SwSC- zmLS@lC}7$D&K*7Ju*Qr%2sgpU)qwZNzY;bIEc;`)c7i9Rx%T=YxJDYp_?t98ahPQV zdQ1hAKI7W=G?5|2w0P1|m%D)Ah{~^oqp)_-q$;==?o#0)`#ftB@m7HmfS9d{5|^xH)?>=~I$9UlUiCE9c008;!14Ce|)O94Jtx zaA5%np8OKH)5o88t&793j;1MB!BRgTJ&9>ks+d7P8{xfrYRYY{bxDa&!5yx(L|omV z1u%Tc2o2K-ZOVGrS{g$>qVhJ^zHcv_LhVckQ=Wu7;IJ~I&z#|D{@0m7&r0}}cc`Zs z{tGUof=1;LxC<^+{xdu@A)gwMyTRaHn&%S&MG;`r1fWT^3F z_@x{b!s_y=L&%GL@>4uZFA4Sx`UI|rH5P+2Veyd4gLC9jd+?AkFnEF7J_ffZIBo@t zP>@c7PB6hQyPPh)~hbPC0OY4=_z+JEgQK+3q$w`c;i$iO7IilxQHCg{SqAZMKO%$meF4u2214Ch)eM5foV#j}E4Rt2M`frYbH6euz(&AR-^Qb^tIMWy~lZm4)$dsWi zp7{mWUeks=Rpo4M@4!QWBrVTIXswjj^?xRZf!aA&GvY{}C%&PDWd6*sYM&*-uXbkomKn#6f8epkBewqvoWJoqGNDjEx zDl&L;eR4ALq=^cW8D^^nU{#R(Sv(}=wF{G%bFM7&f_f!U zDF@M5#A1M1R`TtxbvJz)@CvMTtsm~DK)1?UU8|u2d5ZEb*IHA9yi>Ugrp4YKG5G}y ztV8Y@$mPIVx=8n;aVzX3QxaPdK z28BzEJY@mr0`0JRD1{54bt&W@Q~8y|jC4t2kI48>!6o;FNodjx|4N1;SS?OrF)~N$ zDqrPV>!ZjAl$+pDSf}!oopO8*dj@d*V2;r>KU1+eK8NKBDg~EpXre&1W*CHr;MK}Z zL>qDWI5RbG?1w!CpK$YO$wZf=PNf0jV)an!EDp-0ysizYTp~8r!USbp#(nDN<@szR z)ER}T%g8VQ%hW3JugXa~Z^J6T8F?>q^-wAkPb(-ldP)qXa?RKzU8q5wx(P1!)yML^ zl`gP*bp21=L!XO6uRu541A8s_3Adgefpua^{ki-|FkGSvM_^ItDu3wO_nv`u*rqY6 zwoA;k7F&BfA#Q!W7Y4h3PhzE2kPGj}jE-h3Sr(H&YsLCaO8Tp%i`PrtoOU$T2 zX}7Vs5VYgE%_NXvyuePl(Z^3=K(4YFaFkIn0_((*_A-1N z_FDW4ZhK9EHHOmIly+cnpj8d{fEdB!@$m`>qFqv;!DN^<4Hl0Cm9ymI4ETidVs=a= zurvUJW^p0&$VzxfecgY7GaE~%Ev3}57{|T~L9JM@33%zrJS&wmt?FoWJ zgKpL{+)X=-yj&}ImAhsOaA{PT#b-?&3UuC>#Z>!bIr;aioS0Zsid?S^(}{_Fry2i~ zhE0a~qqL}k4BDdfnQ$2_qktBsGxeHJT}=M;B5s8ZBR{Fka(#~|(A6&e>&Qo7?ep|+ zBd>zBi)Iw?n;voL<6^B~6&r$XSYsmnUNZF1f+Cf-xYi*TdAsrso;BDE_bb0BzcT_i zE90SO2jK?gw_K|m1G%x2N@>tAqik)E4C=Dw~TE}4TVsAh(cXlX76Cf(gIJzJYr(@9@qNu zUJfuKs_=RG@Dy_G!`VM|t(STy)`r<{(ttYT+6A+J?OHF_BiEpvEioVlL`?k|0oMP1 zToOn(3Nl!3@986h4y%kDhT&>hyCfs#TCYhTF46pja4TY+crvaes*fYr4MzqZctg5a z=aE^=d_T{HwaYW&w~;`FfiA5O7yTlM#;Zp%h=JorC~!vQ54zTyU9k3X#v`uvRv`v> z%wqGl7Ilgoz6SJp(eoLVbH-(?xMBvw}XCAX1lS zvivqoKd$hf$YD=mWV+nU z$zrJV^FUDBlBo+4F%*%2L%y1!WDy5LF6)`_9U!P9a)ZCf=GvLU;$@ z!u7CTYDFH#pk`Rt|H$K9n21YtSVg+|1X21}qdf8&Ef#|{2qSOGCyeAT)b&5|JJ&uS zEsSalK6dSfAgnFUn&#S1xD)a$mg}dhd|bp0W7W(P^`-$?3`6%jf%FRGI!v>;xV(oJdmhN@ zg!lV+KR2FJ6!`cbY@yO& z-63U%_$d~GC=ny{dG>5ZiNuU2$3yGHnZ1qr#Q>d0vi}W(VzT~w z79PYRQQ#TGuxt_+dGc4`7I?ZUJkF>ohUY7bOX+LxVm12@1YHDHk7hIXH=KbpP5zw8 z@G$y41v$8|p&!oD40&*uufXSEX@MG)v)r|RP(zTlYkr1dLlbhHXmY;Ev3&;|Lym`Y zzKy&dRuAT^cI`cJu~;mW3{7kr^Wg^NtrVz*C2DbT4lX+ogez2jlp__&d&m4a1X*Xy zSKm977mw=9n)5%fbdh#R&PPmqqsO%XQJFgxmlgOpgBFMZg+^g+3@-BV#pI9r@?Q>1 z%&5zASHOin<^ZEX#m=_9HcL zpK1J@Wqd9VC%jE>&AEog_(s?q)~y%wo$8;2u8nt&Qcew>L$4eigHuey=5Qo%um!Hr zdjAYdL!{x1?T;Mu<8JhWN8pn_g`dDa1Lvm3N$@F{yU4Zo`zoCKMc7y2+)CJ2 z;oRF{e}(H{e|-*IHDSQqL$3Xlzy5eX34R4k4Nv(MxUf$_l(>J&ryx29_9=*RQn$~i zAj(dEpRfKXbK^c={qgACB={98I6@!lD40K&^)Ct5;Xj|rc2DiGQ+=U(1MyE}RxV0oEn>ERoJpbw zmUUg2N$^k=7?_tBKJz(~Jlx@9hTEYEIEg{e@S6Wy_A;W#W7iElT@PDx5+i#)(R{dJ zLcSo0;dDqcBpC%fV#7ZC2&@Vh%yR=*?}kf_feV(pfrEQtt#Cn!8`v*$kC_w?CniZ_ z(1IJ?z=|Qnr?ou$-NTKr8n|Fx+zm*5cA2qo!Ts{xHrBx=E#SC;>Sp#zG37%T&`$wR z;W2nv*m!KgOSCv&SnGS;)vuFj`M2D__gp67e3i%FWq&CyRSzVyy$e&9Rjvity&bBC z)73+NqI44u@)Ul^MbH`Ko`GX>ry9J(l#flJ#eJ~WAB(v5D@E{RJjCC4>;l&cR*+DR zR{A`4DFqI|wow>kYVO(%-)dYMyIyX8g9lZ?Ep*Wc-9M~cPkC7jW@`Ri6kbl%VWTj% z5sxP!Hy(;xu`O~V9URmOI_T53@eEe%38FQzxM+n3U5*u4ms~aYc{i}BnFfufC4wyW z6E|?6(^sA@SVx~v399@}43sVkN(0%W#28M8#btd)!3<`gAC$tmFsomTs6Jc^YtY30 zih=d8_Hm4>xWoP6EaS1f0K2=-iQKd}Zz{u2x=_mVH;=m<_tXi~;LC7%UX&O(A_-c- z`2-P-^AzM=LXcJYxEO`aPVUb_=PjFhob+PjiM(=JE{3XMc}zryp9Xa_<=qO8!rG9$ zyWl3L@n?LCpAeiR#xaWX9wB)**@DVA_@J-7dA%&wCCHdG=oMQa9@NzeBWsv+QjKYO%jSlZe?kQwRIlRlqe5xHdQ0;WS&NmfHoL*kO z97Y^gbGDPc#aI0<+$`~-ZeF;T?t2lu$@ps_^Y`HmPVNA1Jx#KX%!R)p`7Su1+5SWr z)xcWKBAZsVg4(J@>3E?YE;FrIbe_pSslj+*5i9?zcqOli`uMwO2{Q#2TluE`MR*~; ze~<)E!4(+L3wjD}pu!QK{6g(lnOf-AMK_awCvtV$qT6Y=_{FPlHDOxi;~$a#`;uP` z*vjmFxQ+yk>qYD>4z&quh9}&>RmWlV(4rm;YJxolFQZW6z~eW#vg`sq1#hEJSo?hu z12u8w={4YeX7`;@GL)zbCGu9oo&jTUJ**YxPsimouuet!*>2z){RC2bBP3`G@~@#qg+9KKuYES5pfZI*blq)k;7%en zP-^-(zmB2T=gWULlWhcUQG>QHwN$}k0Dtpk=t})+P)7bfF66{T;u7Yvd}6b^=h)yI zq3h2N3_H;e`7U^uQ~zOT?zAq;-cZ0K*6s9xldCPeW60_KFtlJYzpB%5%Dm-Ai8{v2 z+g8io88&Y_d7JB0|1Wu|WoLoYEN>&c?Qpu}Z9Z@JJ169A32*zHF?ri#+0Pd_v5!LY zr##okd%pjgct61VGH12CXCC1)%<23naa%w?FO&J5r?Lwi_anZ&|Cp10hLrbt0LFT! zn+a delta 224920 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

mA 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 diff --git a/roms/seabios-hppa b/roms/seabios-hppa index 458626c4c6..1cfbe76ff6 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit 458626c4c6441045c0612f24313c7cf1f95e71c6 +Subproject commit 1cfbe76ff625fce9ed5991f7e13d80ffec900f40 From c5ffd16ba4c8fd3601742cc9d2b3cff03995dd5d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 20 Jun 2023 18:57:12 +0100 Subject: [PATCH 412/412] Revert "cputlb: Restrict SavedIOTLB to system emulation" This reverts commit d7ee93e24359703debf4137f4cc632563aa4e8d1. That commit tries to make a field in the CPUState struct not be present when CONFIG_USER_ONLY is set. Unfortunately, you can't conditionally omit fields in structs like this based on ifdefs that are set per-target. If you try it, then code in files compiled per-target (where CONFIG_USER_ONLY is or can be set) will disagree about the struct layout with files that are compiled once-only (where this kind of ifdef is never set). This manifests specifically in 'make check-tcg' failing, because code in cpus-common.c that sets up the CPUState::cpu_index field puts it at a different offset from the code in plugins/core.c in qemu_plugin_vcpu_init_hook() which reads the cpu_index field. The latter then hits an assert because from its point of view every thread has a 0 cpu_index. There might be other weird behaviour too. Mostly we catch this kind of bug because the CONFIG_whatever is listed in include/exec/poison.h and so the reference to it in build-once source files will then cause a compiler error. Unfortunately CONFIG_USER_ONLY is an exception to that: we have some places where we use it in "safe" ways in headers that will be seen by once-only source files (e.g. ifdeffing out function prototypes) and it would be a lot of refactoring to be able to get to a position where we could poison it. This leaves us in a "you have to be careful to walk around the bear trap" situation... Fixes: d7ee93e243597 ("cputlb: Restrict SavedIOTLB to system emulation") Signed-off-by: Peter Maydell Message-Id: <20230620175712.1331625-1-peter.maydell@linaro.org> Signed-off-by: Richard Henderson --- include/hw/core/cpu.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index ee8d6b40b3..4871ad85f0 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -226,7 +226,7 @@ struct CPUWatchpoint { QTAILQ_ENTRY(CPUWatchpoint) entry; }; -#if defined(CONFIG_PLUGIN) && !defined(CONFIG_USER_ONLY) +#ifdef CONFIG_PLUGIN /* * For plugins we sometime need to save the resolved iotlb data before * the memory regions get moved around by io_writex. @@ -410,11 +410,9 @@ struct CPUState { #ifdef CONFIG_PLUGIN GArray *plugin_mem_cbs; -#if !defined(CONFIG_USER_ONLY) /* saved iotlb data from io_writex */ SavedIOTLB saved_iotlb; -#endif /* !CONFIG_USER_ONLY */ -#endif /* CONFIG_PLUGIN */ +#endif /* TODO Move common fields from CPUArchState here. */ int cpu_index;