diff options
59 files changed, 3646 insertions, 2567 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a2da141a92..e170a4c733 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -259,7 +259,6 @@ F: include/hw/ppc/ F: disas/ppc.c RISC-V -M: Michael Clark <mjc@sifive.com> M: Palmer Dabbelt <palmer@sifive.com> M: Alistair Francis <Alistair.Francis@wdc.com> M: Sagar Karandikar <sagark@eecs.berkeley.edu> diff --git a/chardev/char-fe.c b/chardev/char-fe.c index a8931f7afd..f3530a90e6 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -246,14 +246,15 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del) } } -void qemu_chr_fe_set_handlers(CharBackend *b, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - BackendChangeHandler *be_change, - void *opaque, - GMainContext *context, - bool set_open) +void qemu_chr_fe_set_handlers_full(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + BackendChangeHandler *be_change, + void *opaque, + GMainContext *context, + bool set_open, + bool sync_state) { Chardev *s; int fe_open; @@ -285,14 +286,24 @@ void qemu_chr_fe_set_handlers(CharBackend *b, qemu_chr_fe_take_focus(b); /* We're connecting to an already opened device, so let's make sure we also get the open event */ - if (s->be_open) { + if (sync_state && s->be_open) { qemu_chr_be_event(s, CHR_EVENT_OPENED); } } +} - if (CHARDEV_IS_MUX(s)) { - mux_chr_set_handlers(s, context); - } +void qemu_chr_fe_set_handlers(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + BackendChangeHandler *be_change, + void *opaque, + GMainContext *context, + bool set_open) +{ + qemu_chr_fe_set_handlers_full(b, fd_can_read, fd_read, fd_event, be_change, + opaque, context, set_open, + true); } void qemu_chr_fe_take_focus(CharBackend *b) diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 6055e76293..23aa82125d 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -278,18 +278,18 @@ static void char_mux_finalize(Object *obj) qemu_chr_fe_deinit(&d->chr, false); } -void mux_chr_set_handlers(Chardev *chr, GMainContext *context) +static void mux_chr_update_read_handlers(Chardev *chr) { MuxChardev *d = MUX_CHARDEV(chr); /* Fix up the real driver with mux routines */ - qemu_chr_fe_set_handlers(&d->chr, - mux_chr_can_read, - mux_chr_read, - mux_chr_event, - NULL, - chr, - context, true); + qemu_chr_fe_set_handlers_full(&d->chr, + mux_chr_can_read, + mux_chr_read, + mux_chr_event, + NULL, + chr, + chr->gcontext, true, false); } void mux_set_focus(Chardev *chr, int focus) @@ -367,7 +367,7 @@ static int open_muxes(Chardev *chr) * mark mux as OPENED so any new FEs will immediately receive * OPENED event */ - qemu_chr_be_event(chr, CHR_EVENT_OPENED); + chr->be_open = 1; return 0; } @@ -383,6 +383,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data) cc->chr_add_watch = mux_chr_add_watch; cc->chr_be_event = mux_chr_be_event; cc->chr_machine_done = open_muxes; + cc->chr_update_read_handler = mux_chr_update_read_handlers; } static const TypeInfo char_mux_type_info = { diff --git a/chardev/char-pty.c b/chardev/char-pty.c index f681d637c1..b034332edd 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -36,15 +36,12 @@ typedef struct { QIOChannel *ioc; int read_bytes; - /* Protected by the Chardev chr_write_lock. */ int connected; GSource *timer_src; - GSource *open_source; } PtyChardev; #define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY) -static void pty_chr_update_read_handler_locked(Chardev *chr); static void pty_chr_state(Chardev *chr, int connected); static void pty_chr_timer_cancel(PtyChardev *s) @@ -56,32 +53,19 @@ static void pty_chr_timer_cancel(PtyChardev *s) } } -static void pty_chr_open_src_cancel(PtyChardev *s) -{ - if (s->open_source) { - g_source_destroy(s->open_source); - g_source_unref(s->open_source); - s->open_source = NULL; - } -} - static gboolean pty_chr_timer(gpointer opaque) { struct Chardev *chr = CHARDEV(opaque); PtyChardev *s = PTY_CHARDEV(opaque); - qemu_mutex_lock(&chr->chr_write_lock); pty_chr_timer_cancel(s); - pty_chr_open_src_cancel(s); if (!s->connected) { /* Next poll ... */ - pty_chr_update_read_handler_locked(chr); + qemu_chr_be_update_read_handlers(chr, chr->gcontext); } - qemu_mutex_unlock(&chr->chr_write_lock); return FALSE; } -/* Called with chr_write_lock held. */ static void pty_chr_rearm_timer(Chardev *chr, int ms) { PtyChardev *s = PTY_CHARDEV(chr); @@ -94,8 +78,7 @@ static void pty_chr_rearm_timer(Chardev *chr, int ms) g_free(name); } -/* Called with chr_write_lock held. */ -static void pty_chr_update_read_handler_locked(Chardev *chr) +static void pty_chr_update_read_handler(Chardev *chr) { PtyChardev *s = PTY_CHARDEV(chr); GPollFD pfd; @@ -117,24 +100,12 @@ static void pty_chr_update_read_handler_locked(Chardev *chr) } } -static void pty_chr_update_read_handler(Chardev *chr) -{ - qemu_mutex_lock(&chr->chr_write_lock); - pty_chr_update_read_handler_locked(chr); - qemu_mutex_unlock(&chr->chr_write_lock); -} - -/* Called with chr_write_lock held. */ static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len) { PtyChardev *s = PTY_CHARDEV(chr); if (!s->connected) { - /* guest sends data, check for (re-)connect */ - pty_chr_update_read_handler_locked(chr); - if (!s->connected) { - return len; - } + return len; } return io_channel_send(s->ioc, buf, len); } @@ -183,23 +154,11 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static gboolean qemu_chr_be_generic_open_func(gpointer opaque) -{ - Chardev *chr = CHARDEV(opaque); - PtyChardev *s = PTY_CHARDEV(opaque); - - s->open_source = NULL; - qemu_chr_be_event(chr, CHR_EVENT_OPENED); - return FALSE; -} - -/* Called with chr_write_lock held. */ static void pty_chr_state(Chardev *chr, int connected) { PtyChardev *s = PTY_CHARDEV(chr); if (!connected) { - pty_chr_open_src_cancel(s); remove_fd_in_watch(chr); s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. @@ -209,13 +168,8 @@ static void pty_chr_state(Chardev *chr, int connected) } else { pty_chr_timer_cancel(s); if (!s->connected) { - g_assert(s->open_source == NULL); - s->open_source = g_idle_source_new(); s->connected = 1; - g_source_set_callback(s->open_source, - qemu_chr_be_generic_open_func, - chr, NULL); - g_source_attach(s->open_source, chr->gcontext); + qemu_chr_be_event(chr, CHR_EVENT_OPENED); } if (!chr->gsource) { chr->gsource = io_add_watch_poll(chr, s->ioc, @@ -231,11 +185,9 @@ static void char_pty_finalize(Object *obj) Chardev *chr = CHARDEV(obj); PtyChardev *s = PTY_CHARDEV(obj); - qemu_mutex_lock(&chr->chr_write_lock); pty_chr_state(chr, 0); object_unref(OBJECT(s->ioc)); pty_chr_timer_cancel(s); - qemu_mutex_unlock(&chr->chr_write_lock); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 3299b46853..a8bae31b8d 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -57,7 +57,7 @@ static void qmp_chardev_open_serial(Chardev *chr, static void tty_serial_init(int fd, int speed, int parity, int data_bits, int stop_bits) { - struct termios tty; + struct termios tty = {0}; speed_t spd; #if 0 diff --git a/chardev/char-socket.c b/chardev/char-socket.c index eaa8e8b68f..4fcdd8aedd 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -46,6 +46,12 @@ typedef struct { size_t buflen; } TCPChardevTelnetInit; +typedef enum { + TCP_CHARDEV_STATE_DISCONNECTED, + TCP_CHARDEV_STATE_CONNECTING, + TCP_CHARDEV_STATE_CONNECTED, +} TCPChardevState; + typedef struct { Chardev parent; QIOChannel *ioc; /* Client I/O channel */ @@ -53,7 +59,7 @@ typedef struct { QIONetListener *listener; GSource *hup_source; QCryptoTLSCreds *tls_creds; - int connected; + TCPChardevState state; int max_size; int do_telnetopt; int do_nodelay; @@ -74,6 +80,8 @@ typedef struct { GSource *reconnect_timer; int64_t reconnect_time; bool connect_err_reported; + + QIOTask *connect_task; } SocketChardev; #define SOCKET_CHARDEV(obj) \ @@ -82,6 +90,21 @@ typedef struct { static gboolean socket_reconnect_timeout(gpointer opaque); static void tcp_chr_telnet_init(Chardev *chr); +static void tcp_chr_change_state(SocketChardev *s, TCPChardevState state) +{ + switch (state) { + case TCP_CHARDEV_STATE_DISCONNECTED: + break; + case TCP_CHARDEV_STATE_CONNECTING: + assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED); + break; + case TCP_CHARDEV_STATE_CONNECTED: + assert(s->state == TCP_CHARDEV_STATE_CONNECTING); + break; + } + s->state = state; +} + static void tcp_chr_reconn_timer_cancel(SocketChardev *s) { if (s->reconnect_timer) { @@ -96,7 +119,8 @@ static void qemu_chr_socket_restart_timer(Chardev *chr) SocketChardev *s = SOCKET_CHARDEV(chr); char *name; - assert(s->connected == 0); + assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED); + assert(!s->reconnect_timer); name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label); s->reconnect_timer = qemu_chr_timeout_add_ms(chr, s->reconnect_time * 1000, @@ -131,7 +155,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (s->connected) { + if (s->state == TCP_CHARDEV_STATE_CONNECTED) { int ret = io_channel_send_full(s->ioc, buf, len, s->write_msgfds, s->write_msgfds_num); @@ -164,7 +188,7 @@ static int tcp_chr_read_poll(void *opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); - if (!s->connected) { + if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return 0; } s->max_size = qemu_chr_be_can_write(chr); @@ -277,7 +301,7 @@ static int tcp_set_msgfds(Chardev *chr, int *fds, int num) s->write_msgfds = NULL; s->write_msgfds_num = 0; - if (!s->connected || + if ((s->state != TCP_CHARDEV_STATE_CONNECTED) || !qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { return -1; @@ -389,7 +413,7 @@ static void tcp_chr_free_connection(Chardev *chr) s->ioc = NULL; g_free(chr->filename); chr->filename = NULL; - s->connected = 0; + tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); } static const char *qemu_chr_socket_protocol(SocketChardev *s) @@ -442,12 +466,12 @@ static void update_disconnected_filename(SocketChardev *s) /* NB may be called even if tcp_chr_connect has not been * reached, due to TLS or telnet initialization failure, - * so can *not* assume s->connected == true + * so can *not* assume s->state == TCP_CHARDEV_STATE_CONNECTED */ static void tcp_chr_disconnect(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); - bool emit_close = s->connected; + bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED; tcp_chr_free_connection(chr); @@ -471,7 +495,8 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) uint8_t buf[CHR_READ_BUF_LEN]; int len, size; - if (!s->connected || s->max_size <= 0) { + if ((s->state != TCP_CHARDEV_STATE_CONNECTED) || + s->max_size <= 0) { return TRUE; } len = sizeof(buf); @@ -508,7 +533,7 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) SocketChardev *s = SOCKET_CHARDEV(chr); int size; - if (!s->connected) { + if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return 0; } @@ -564,7 +589,7 @@ static void update_ioc_handlers(SocketChardev *s) { Chardev *chr = CHARDEV(s); - if (!s->connected) { + if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return; } @@ -589,7 +614,7 @@ static void tcp_chr_connect(void *opaque) g_free(chr->filename); chr->filename = qemu_chr_compute_filename(s); - s->connected = 1; + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTED); update_ioc_handlers(s); qemu_chr_be_event(chr, CHR_EVENT_OPENED); } @@ -828,7 +853,7 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (s->ioc != NULL) { + if (s->state != TCP_CHARDEV_STATE_CONNECTING) { return -1; } @@ -865,11 +890,17 @@ static int tcp_chr_add_client(Chardev *chr, int fd) { int ret; QIOChannelSocket *sioc; + SocketChardev *s = SOCKET_CHARDEV(chr); + + if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) { + return -1; + } sioc = qio_channel_socket_new_fd(fd, NULL); if (!sioc) { return -1; } + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, sioc); ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); @@ -881,35 +912,125 @@ static void tcp_chr_accept(QIONetListener *listener, void *opaque) { Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(chr); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, cioc); tcp_chr_new_client(chr, cioc); } -static int tcp_chr_wait_connected(Chardev *chr, Error **errp) + +static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + QIOChannelSocket *sioc = qio_channel_socket_new(); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); + tcp_chr_set_client_ioc_name(chr, sioc); + if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { + tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); + object_unref(OBJECT(sioc)); + return -1; + } + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); + return 0; +} + + +static void tcp_chr_accept_server_sync(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelSocket *sioc; + info_report("QEMU waiting for connection on: %s", + chr->filename); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); + sioc = qio_net_listener_wait_client(s->listener); + tcp_chr_set_client_ioc_name(chr, sioc); + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); +} + - /* It can't wait on s->connected, since it is set asynchronously - * in TLS and telnet cases, only wait for an accepted socket */ - while (!s->ioc) { +static int tcp_chr_wait_connected(Chardev *chr, Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + const char *opts[] = { "telnet", "tn3270", "websock", "tls-creds" }; + bool optset[] = { s->is_telnet, s->is_tn3270, s->is_websock, s->tls_creds }; + size_t i; + + QEMU_BUILD_BUG_ON(G_N_ELEMENTS(opts) != G_N_ELEMENTS(optset)); + for (i = 0; i < G_N_ELEMENTS(opts); i++) { + if (optset[i]) { + error_setg(errp, + "'%s' option is incompatible with waiting for " + "connection completion", opts[i]); + return -1; + } + } + + tcp_chr_reconn_timer_cancel(s); + + /* + * We expect states to be as follows: + * + * - server + * - wait -> CONNECTED + * - nowait -> DISCONNECTED + * - client + * - reconnect == 0 -> CONNECTED + * - reconnect != 0 -> CONNECTING + * + */ + if (s->state == TCP_CHARDEV_STATE_CONNECTING) { + if (!s->connect_task) { + error_setg(errp, + "Unexpected 'connecting' state without connect task " + "while waiting for connection completion"); + return -1; + } + /* + * tcp_chr_wait_connected should only ever be run from the + * main loop thread associated with chr->gcontext, otherwise + * qio_task_wait_thread has a dangerous race condition with + * free'ing of the s->connect_task object. + * + * Acquiring the main context doesn't 100% prove we're in + * the main loop thread, but it does at least guarantee + * that the main loop won't be executed by another thread + * avoiding the race condition with the task idle callback. + */ + g_main_context_acquire(chr->gcontext); + qio_task_wait_thread(s->connect_task); + g_main_context_release(chr->gcontext); + + /* + * The completion callback (qemu_chr_socket_connected) for + * s->connect_task should have set this to NULL by the time + * qio_task_wait_thread has returned. + */ + assert(!s->connect_task); + + /* + * NB we are *not* guaranteed to have "s->state == ..CONNECTED" + * at this point as this first connect may be failed, so + * allow the next loop to run regardless. + */ + } + + while (s->state != TCP_CHARDEV_STATE_CONNECTED) { if (s->is_listen) { - info_report("QEMU waiting for connection on: %s", - chr->filename); - sioc = qio_net_listener_wait_client(s->listener); - tcp_chr_set_client_ioc_name(chr, sioc); - tcp_chr_new_client(chr, sioc); - object_unref(OBJECT(sioc)); + tcp_chr_accept_server_sync(chr); } else { - sioc = qio_channel_socket_new(); - tcp_chr_set_client_ioc_name(chr, sioc); - if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { - object_unref(OBJECT(sioc)); - return -1; + Error *err = NULL; + if (tcp_chr_connect_client_sync(chr, &err) < 0) { + if (s->reconnect_time) { + error_free(err); + g_usleep(s->reconnect_time * 1000ULL * 1000ULL); + } else { + error_propagate(errp, err); + return -1; + } } - tcp_chr_new_client(chr, sioc); - object_unref(OBJECT(sioc)); } } @@ -945,7 +1066,10 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) SocketChardev *s = SOCKET_CHARDEV(chr); Error *err = NULL; + s->connect_task = NULL; + if (qio_task_propagate_error(task, &err)) { + tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); check_report_connect_error(chr, err); error_free(err); goto cleanup; @@ -958,16 +1082,45 @@ cleanup: object_unref(OBJECT(sioc)); } -static void tcp_chr_connect_async(Chardev *chr) + +static void tcp_chr_connect_client_task(QIOTask *task, + gpointer opaque) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); + SocketAddress *addr = opaque; + Error *err = NULL; + + qio_channel_socket_connect_sync(ioc, addr, &err); + + qio_task_set_error(task, err); +} + + +static void tcp_chr_connect_client_async(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelSocket *sioc; + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); - qio_channel_socket_connect_async(sioc, s->addr, - qemu_chr_socket_connected, - chr, NULL, chr->gcontext); + /* + * Normally code would use the qio_channel_socket_connect_async + * method which uses a QIOTask + qio_task_set_error internally + * to avoid blocking. The tcp_chr_wait_connected method, however, + * needs a way to synchronize with completion of the background + * connect task which can't be done with the QIOChannelSocket + * async APIs. Thus we must use QIOTask directly to implement + * the non-blocking concept locally. + */ + s->connect_task = qio_task_new(OBJECT(sioc), + qemu_chr_socket_connected, + chr, NULL); + qio_task_run_in_thread(s->connect_task, + tcp_chr_connect_client_task, + s->addr, + NULL, + chr->gcontext); } static gboolean socket_reconnect_timeout(gpointer opaque) @@ -982,11 +1135,138 @@ static gboolean socket_reconnect_timeout(gpointer opaque) return false; } - tcp_chr_connect_async(chr); + tcp_chr_connect_client_async(chr); return false; } + +static int qmp_chardev_open_socket_server(Chardev *chr, + bool is_telnet, + bool is_waitconnect, + Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + char *name; + if (is_telnet) { + s->do_telnetopt = 1; + } + s->listener = qio_net_listener_new(); + + name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); + qio_net_listener_set_name(s->listener, name); + g_free(name); + + if (qio_net_listener_open_sync(s->listener, s->addr, errp) < 0) { + object_unref(OBJECT(s->listener)); + s->listener = NULL; + return -1; + } + + qapi_free_SocketAddress(s->addr); + s->addr = socket_local_address(s->listener->sioc[0]->fd, errp); + update_disconnected_filename(s); + + if (is_waitconnect) { + tcp_chr_accept_server_sync(chr); + } else { + qio_net_listener_set_client_func_full(s->listener, + tcp_chr_accept, + chr, NULL, + chr->gcontext); + } + + return 0; +} + + +static int qmp_chardev_open_socket_client(Chardev *chr, + int64_t reconnect, + Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + + if (reconnect > 0) { + s->reconnect_time = reconnect; + tcp_chr_connect_client_async(chr); + return 0; + } else { + return tcp_chr_connect_client_sync(chr, errp); + } +} + + +static bool qmp_chardev_validate_socket(ChardevSocket *sock, + SocketAddress *addr, + Error **errp) +{ + /* Validate any options which have a dependency on address type */ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_FD: + if (sock->has_reconnect) { + error_setg(errp, + "'reconnect' option is incompatible with " + "'fd' address type"); + return false; + } + if (sock->has_tls_creds && + !(sock->has_server && sock->server)) { + error_setg(errp, + "'tls_creds' option is incompatible with " + "'fd' address type as client"); + return false; + } + break; + + case SOCKET_ADDRESS_TYPE_UNIX: + if (sock->has_tls_creds) { + error_setg(errp, + "'tls_creds' option is incompatible with " + "'unix' address type"); + return false; + } + break; + + case SOCKET_ADDRESS_TYPE_INET: + break; + + case SOCKET_ADDRESS_TYPE_VSOCK: + if (sock->has_tls_creds) { + error_setg(errp, + "'tls_creds' option is incompatible with " + "'vsock' address type"); + return false; + } + + default: + break; + } + + /* Validate any options which have a dependancy on client vs server */ + if (!sock->has_server || sock->server) { + if (sock->has_reconnect) { + error_setg(errp, + "'reconnect' option is incompatible with " + "socket in server listen mode"); + return false; + } + } else { + if (sock->has_websocket && sock->websocket) { + error_setg(errp, "%s", "Websocket client is not implemented"); + return false; + } + if (sock->has_wait) { + error_setg(errp, "%s", + "'wait' option is incompatible with " + "socket in client connect mode"); + return false; + } + } + + return true; +} + + static void qmp_chardev_open_socket(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -1001,14 +1281,8 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_waitconnect = sock->has_wait ? sock->wait : false; bool is_websock = sock->has_websocket ? sock->websocket : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; - QIOChannelSocket *sioc = NULL; SocketAddress *addr; - if (!is_listen && is_websock) { - error_setg(errp, "%s", "Websocket client is not implemented"); - goto error; - } - s->is_listen = is_listen; s->is_telnet = is_telnet; s->is_tn3270 = is_tn3270; @@ -1021,7 +1295,7 @@ static void qmp_chardev_open_socket(Chardev *chr, if (!creds) { error_setg(errp, "No TLS credentials with id '%s'", sock->tls_creds); - goto error; + return; } s->tls_creds = (QCryptoTLSCreds *) object_dynamic_cast(creds, @@ -1029,30 +1303,30 @@ static void qmp_chardev_open_socket(Chardev *chr, if (!s->tls_creds) { error_setg(errp, "Object with id '%s' is not TLS credentials", sock->tls_creds); - goto error; + return; } object_ref(OBJECT(s->tls_creds)); if (is_listen) { if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { error_setg(errp, "%s", "Expected TLS credentials for server endpoint"); - goto error; + return; } } else { if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { error_setg(errp, "%s", "Expected TLS credentials for client endpoint"); - goto error; + return; } } } s->addr = addr = socket_address_flatten(sock->addr); - if (sock->has_reconnect && addr->type == SOCKET_ADDRESS_TYPE_FD) { - error_setg(errp, "'reconnect' option is incompatible with 'fd'"); - goto error; + if (!qmp_chardev_validate_socket(sock, addr, errp)) { + return; } + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE); /* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */ if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) { @@ -1064,73 +1338,25 @@ static void qmp_chardev_open_socket(Chardev *chr, update_disconnected_filename(s); - if (is_listen) { - if (is_telnet || is_tn3270) { - s->do_telnetopt = 1; + if (s->is_listen) { + if (qmp_chardev_open_socket_server(chr, is_telnet || is_tn3270, + is_waitconnect, errp) < 0) { + return; } - } else if (reconnect > 0) { - s->reconnect_time = reconnect; - } - - if (s->reconnect_time) { - tcp_chr_connect_async(chr); } else { - if (s->is_listen) { - char *name; - s->listener = qio_net_listener_new(); - - name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); - qio_net_listener_set_name(s->listener, name); - g_free(name); - - if (qio_net_listener_open_sync(s->listener, s->addr, errp) < 0) { - object_unref(OBJECT(s->listener)); - s->listener = NULL; - goto error; - } - - qapi_free_SocketAddress(s->addr); - s->addr = socket_local_address(s->listener->sioc[0]->fd, errp); - update_disconnected_filename(s); - - if (is_waitconnect && - qemu_chr_wait_connected(chr, errp) < 0) { - return; - } - if (!s->ioc) { - qio_net_listener_set_client_func_full(s->listener, - tcp_chr_accept, - chr, NULL, - chr->gcontext); - } - } else if (qemu_chr_wait_connected(chr, errp) < 0) { - goto error; + if (qmp_chardev_open_socket_client(chr, reconnect, errp) < 0) { + return; } } - - return; - -error: - if (sioc) { - object_unref(OBJECT(sioc)); - } } static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, Error **errp) { - bool is_listen = qemu_opt_get_bool(opts, "server", false); - bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true); - bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); - bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false); - bool is_websock = qemu_opt_get_bool(opts, "websocket", false); - bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); - int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0); const char *path = qemu_opt_get(opts, "path"); const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); const char *fd = qemu_opt_get(opts, "fd"); - const char *tls_creds = qemu_opt_get(opts, "tls-creds"); SocketAddressLegacy *addr; ChardevSocket *sock; @@ -1140,45 +1366,39 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, return; } - backend->type = CHARDEV_BACKEND_KIND_SOCKET; - if (path) { - if (tls_creds) { - error_setg(errp, "TLS can only be used over TCP socket"); - return; - } - } else if (host) { - if (!port) { - error_setg(errp, "chardev: socket: no port given"); - return; - } - } else if (fd) { - /* We don't know what host to validate against when in client mode */ - if (tls_creds && !is_listen) { - error_setg(errp, "TLS can not be used with pre-opened client FD"); - return; - } - } else { - g_assert_not_reached(); + if (host && !port) { + error_setg(errp, "chardev: socket: no port given"); + return; } + backend->type = CHARDEV_BACKEND_KIND_SOCKET; sock = backend->u.socket.data = g_new0(ChardevSocket, 1); qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock)); - sock->has_nodelay = true; - sock->nodelay = do_nodelay; + sock->has_nodelay = qemu_opt_get(opts, "delay"); + sock->nodelay = !qemu_opt_get_bool(opts, "delay", true); + /* + * We have different default to QMP for 'server', hence + * we can't just check for existence of 'server' + */ sock->has_server = true; - sock->server = is_listen; - sock->has_telnet = true; - sock->telnet = is_telnet; - sock->has_tn3270 = true; - sock->tn3270 = is_tn3270; - sock->has_websocket = true; - sock->websocket = is_websock; - sock->has_wait = true; - sock->wait = is_waitconnect; + sock->server = qemu_opt_get_bool(opts, "server", false); + sock->has_telnet = qemu_opt_get(opts, "telnet"); + sock->telnet = qemu_opt_get_bool(opts, "telnet", false); + sock->has_tn3270 = qemu_opt_get(opts, "tn3270"); + sock->tn3270 = qemu_opt_get_bool(opts, "tn3270", false); + sock->has_websocket = qemu_opt_get(opts, "websocket"); + sock->websocket = qemu_opt_get_bool(opts, "websocket", false); + /* + * We have different default to QMP for 'wait' when 'server' + * is set, hence we can't just check for existence of 'wait' + */ + sock->has_wait = qemu_opt_find(opts, "wait") || sock->server; + sock->wait = qemu_opt_get_bool(opts, "wait", true); sock->has_reconnect = qemu_opt_find(opts, "reconnect"); - sock->reconnect = reconnect; - sock->tls_creds = g_strdup(tls_creds); + sock->reconnect = qemu_opt_get_number(opts, "reconnect", 0); + sock->has_tls_creds = qemu_opt_get(opts, "tls-creds"); + sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds")); addr = g_new0(SocketAddressLegacy, 1); if (path) { @@ -1223,7 +1443,7 @@ char_socket_get_connected(Object *obj, Error **errp) { SocketChardev *s = SOCKET_CHARDEV(obj); - return s->connected; + return s->state == TCP_CHARDEV_STATE_CONNECTED; } static void char_socket_class_init(ObjectClass *oc, void *data) diff --git a/chardev/char.c b/chardev/char.c index ccba36bafb..f6d61fa5f8 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -490,6 +490,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename, return opts; } + error_report("'%s' is not a valid char driver", filename); + fail: qemu_opts_del(opts); return NULL; @@ -634,7 +636,8 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) return backend; } -Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, + Error **errp) { const ChardevClass *cc; Chardev *chr = NULL; @@ -674,7 +677,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) chr = qemu_chardev_new(bid ? bid : id, object_class_get_name(OBJECT_CLASS(cc)), - backend, errp); + backend, context, errp); if (chr == NULL) { goto out; @@ -687,7 +690,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) backend->type = CHARDEV_BACKEND_KIND_MUX; backend->u.mux.data = g_new0(ChardevMux, 1); backend->u.mux.data->chardev = g_strdup(bid); - mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp); + mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, context, errp); if (mux == NULL) { object_unparent(OBJECT(chr)); chr = NULL; @@ -703,7 +706,7 @@ out: } Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, - bool permit_mux_mon) + bool permit_mux_mon, GMainContext *context) { const char *p; Chardev *chr; @@ -718,7 +721,7 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, &err); + chr = qemu_chr_new_from_opts(opts, context, &err); if (!chr) { error_report_err(err); goto out; @@ -736,10 +739,11 @@ out: static Chardev *qemu_chr_new_permit_mux_mon(const char *label, const char *filename, - bool permit_mux_mon) + bool permit_mux_mon, + GMainContext *context) { Chardev *chr; - chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon); + chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context); if (chr) { if (replay_mode != REPLAY_MODE_NONE) { qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); @@ -753,14 +757,16 @@ static Chardev *qemu_chr_new_permit_mux_mon(const char *label, return chr; } -Chardev *qemu_chr_new(const char *label, const char *filename) +Chardev *qemu_chr_new(const char *label, const char *filename, + GMainContext *context) { - return qemu_chr_new_permit_mux_mon(label, filename, false); + return qemu_chr_new_permit_mux_mon(label, filename, false, context); } -Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename) +Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename, + GMainContext *context) { - return qemu_chr_new_permit_mux_mon(label, filename, true); + return qemu_chr_new_permit_mux_mon(label, filename, true, context); } static int qmp_query_chardev_foreach(Object *obj, void *data) @@ -935,6 +941,7 @@ void qemu_chr_set_feature(Chardev *chr, Chardev *qemu_chardev_new(const char *id, const char *typename, ChardevBackend *backend, + GMainContext *gcontext, Error **errp) { Object *obj; @@ -947,6 +954,7 @@ Chardev *qemu_chardev_new(const char *id, const char *typename, obj = object_new(typename); chr = CHARDEV(obj); chr->label = g_strdup(id); + chr->gcontext = gcontext; qemu_char_open(chr, backend, &be_opened, &local_err); if (local_err) { @@ -991,7 +999,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, } chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), - backend, errp); + backend, NULL, errp); if (!chr) { return NULL; } @@ -1049,7 +1057,7 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, } chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), - backend, errp); + backend, chr->gcontext, errp); if (!chr_new) { return NULL; } diff --git a/chardev/wctablet.c b/chardev/wctablet.c index 969d014574..35dbd29a33 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -177,7 +177,7 @@ static void wctablet_input_sync(DeviceState *dev) } static QemuInputHandler wctablet_handler = { - .name = "QEMU Wacome Pen Tablet", + .name = "QEMU Wacom Pen Tablet", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = wctablet_input_event, .sync = wctablet_input_sync, @@ -2531,7 +2531,7 @@ int gdbserver_start(const char *device) * FIXME: it's a bit weird to allow using a mux chardev here * and implicitly setup a monitor. We may want to break this. */ - chr = qemu_chr_new_noreplay("gdb", device, true); + chr = qemu_chr_new_noreplay("gdb", device, true, NULL); if (!chr) return -1; } @@ -2545,7 +2545,7 @@ int gdbserver_start(const char *device) /* Initialize a monitor terminal for gdb */ mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, - NULL, &error_abort); + NULL, NULL, &error_abort); monitor_init(mon_chr, 0); } else { qemu_chr_fe_deinit(&s->chr, true); @@ -2395,7 +2395,7 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict) if (opts == NULL) { error_setg(&err, "Parsing chardev args failed"); } else { - qemu_chr_new_from_opts(opts, &err); + qemu_chr_new_from_opts(opts, NULL, &err); qemu_opts_del(opts); } hmp_handle_error(mon, &err); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 3c7d1364a9..94dffb2f57 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -799,7 +799,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, s->irq = irq; omap_sti_reset(s); - qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null"), + qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null", NULL), &error_abort); memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti", diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c index 0341ded50c..fa6660a113 100644 --- a/hw/bt/hci-csr.c +++ b/hw/bt/hci-csr.c @@ -501,7 +501,7 @@ static const TypeInfo char_hci_type_info = { Chardev *uart_hci_init(void) { return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI, - NULL, &error_abort); + NULL, NULL, &error_abort); } static void register_types(void) diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 6fd1b9cf6b..b3bb1cfcec 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -63,7 +63,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base, s->irq = irq; s->serial = serial_mm_init(get_system_memory(), base, 2, irq, omap_clk_getrate(fclk)/16, - chr ?: qemu_chr_new(label, "null"), + chr ?: qemu_chr_new(label, "null", NULL), DEVICE_NATIVE_ENDIAN); return s; } @@ -183,6 +183,6 @@ 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"), + chr ?: qemu_chr_new("null", "null", NULL), DEVICE_NATIVE_ENDIAN); } diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index e9c45e55b1..35b079d5c4 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -31,7 +31,7 @@ typedef struct Terminal3270 { uint8_t outv[OUTPUT_BUFFER_SIZE]; int in_len; bool handshake_done; - GSource *timer_src; + guint timer_tag; } Terminal3270; #define TYPE_TERMINAL_3270 "x-terminal3270" @@ -47,10 +47,9 @@ static int terminal_can_read(void *opaque) static void terminal_timer_cancel(Terminal3270 *t) { - if (t->timer_src) { - g_source_destroy(t->timer_src); - g_source_unref(t->timer_src); - t->timer_src = NULL; + if (t->timer_tag) { + g_source_remove(t->timer_tag); + t->timer_tag = 0; } } @@ -100,8 +99,7 @@ static void terminal_read(void *opaque, const uint8_t *buf, int size) assert(size <= (INPUT_BUFFER_SIZE - t->in_len)); terminal_timer_cancel(t); - t->timer_src = qemu_chr_timeout_add_ms(t->chr.chr, 600 * 1000, - send_timing_mark_cb, t); + t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t); memcpy(&t->inv[t->in_len], buf, size); t->in_len += size; if (t->in_len < 2) { @@ -160,8 +158,7 @@ static void chr_event(void *opaque, int event) * char-socket.c. Once qemu receives the terminal-type of the * client, mark handshake done and trigger everything rolling again. */ - t->timer_src = qemu_chr_timeout_add_ms(t->chr.chr, 600 * 1000, - send_timing_mark_cb, t); + t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t); break; case CHR_EVENT_CLOSED: sch->curr_status.scsw.dstat = SCSW_DSTAT_DEVICE_END; diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index dc6ff0e5b3..91f34ef06c 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -211,7 +211,8 @@ static int con_init(struct XenLegacyDevice *xendev) * FIXME: sure we want to support implicit * muxed monitors here? */ - qemu_chr_new_mux_mon(label, output), &error_abort); + qemu_chr_new_mux_mon(label, output, NULL), + &error_abort); } xenstore_store_pv_console_info(con->xendev.dev, diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index 31e09942b5..360716de57 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -105,6 +105,7 @@ typedef struct DinoState { MemoryRegion bm; MemoryRegion bm_ram_alias; MemoryRegion bm_pci_alias; + MemoryRegion bm_cpu_alias; MemoryRegion cpu0_eir_mem; } DinoState; @@ -473,12 +474,17 @@ PCIBus *dino_init(MemoryRegion *addr_space, memory_region_init_alias(&s->bm_pci_alias, OBJECT(s), "bm-pci", &s->pci_mem, 0xf0000000 + DINO_MEM_CHUNK_SIZE, - 31 * DINO_MEM_CHUNK_SIZE); + 30 * DINO_MEM_CHUNK_SIZE); + memory_region_init_alias(&s->bm_cpu_alias, OBJECT(s), + "bm-cpu", addr_space, 0xfff00000, + 0xfffff); memory_region_add_subregion(&s->bm, 0, &s->bm_ram_alias); memory_region_add_subregion(&s->bm, 0xf0000000 + DINO_MEM_CHUNK_SIZE, &s->bm_pci_alias); + memory_region_add_subregion(&s->bm, 0xfff00000, + &s->bm_cpu_alias); address_space_init(&s->bm_as, &s->bm, "pci-bm"); pci_setup_iommu(b, dino_pcihost_set_iommu, s); diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 8bc2f69eaa..d54463bf03 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -44,7 +44,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) chr = parallel_hds[i]; if (chr == NULL) { name = g_strdup_printf("discarding-parallel%d", i); - chr = qemu_chr_new(name, "null"); + chr = qemu_chr_new(name, "null", NULL); } else { name = g_strdup_printf("parallel%d", i); } @@ -84,7 +84,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) chr = serial_hd(i); if (chr == NULL) { name = g_strdup_printf("discarding-serial%d", i); - chr = qemu_chr_new(name, "null"); + chr = qemu_chr_new(name, "null", NULL); } else { name = g_strdup_printf("serial%d", i); } diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 6c9c20a93e..e5bab3cadc 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -512,7 +512,7 @@ static void boston_mach_init(MachineState *machine) memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0); - chr = qemu_chr_new("lcd", "vc:320x240"); + chr = qemu_chr_new("lcd", "vc:320x240", NULL); qemu_chr_fe_init(&s->lcd_display, chr, NULL); qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, boston_lcd_event, NULL, s, NULL, true); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 74667766c2..7a403ef1ce 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -568,7 +568,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, memory_region_add_subregion(address_space, base, &s->iomem_lo); memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi); - chr = qemu_chr_new("fpga", "vc:320x200"); + chr = qemu_chr_new("fpga", "vc:320x200", NULL); qemu_chr_fe_init(&s->display, chr, NULL); qemu_chr_fe_set_handlers(&s->display, NULL, NULL, malta_fgpa_display_event, NULL, s, NULL, true); diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index bfc086609c..b1cd11363c 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -74,7 +74,7 @@ static const struct MemmapEntry { [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } }; -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 2730b25b60..7bc25820fe 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -65,7 +65,7 @@ static const struct MemmapEntry { #define GEM_REVISION 0x10070109 -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index c66ffc50cc..2a000a5800 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -53,7 +53,7 @@ static const struct MemmapEntry { [SPIKE_DRAM] = { 0x80000000, 0x0 }, }; -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 3e8b19c668..fc4c6b306e 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -62,7 +62,7 @@ static const struct MemmapEntry { [VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 }, }; -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 98d1ca3c91..03c3bcd240 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -514,7 +514,7 @@ static USBDevice *usb_braille_init(USBBus *bus, const char *unused) USBDevice *dev; Chardev *cdrv; - cdrv = qemu_chr_new("braille", "braille"); + cdrv = qemu_chr_new("braille", "braille", NULL); if (!cdrv) return NULL; diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 46c997d352..aa1b864ccd 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -67,7 +67,7 @@ bool qemu_chr_fe_backend_connected(CharBackend *be); bool qemu_chr_fe_backend_open(CharBackend *be); /** - * qemu_chr_fe_set_handlers: + * qemu_chr_fe_set_handlers_full: * @b: a CharBackend * @fd_can_read: callback to get the amount of data the frontend may * receive @@ -79,12 +79,28 @@ bool qemu_chr_fe_backend_open(CharBackend *be); * @context: a main loop context or NULL for the default * @set_open: whether to call qemu_chr_fe_set_open() implicitely when * any of the handler is non-NULL + * @sync_state: whether to issue event callback with updated state * * Set the front end char handlers. The front end takes the focus if * any of the handler is non-NULL. * * Without associated Chardev, nothing is changed. */ +void qemu_chr_fe_set_handlers_full(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + BackendChangeHandler *be_change, + void *opaque, + GMainContext *context, + bool set_open, + bool sync_state); + +/** + * qemu_chr_fe_set_handlers: + * + * Version of qemu_chr_fe_set_handlers_full() with sync_state = true. + */ void qemu_chr_fe_set_handlers(CharBackend *b, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, @@ -168,6 +184,9 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) * is active; return the #GSource's tag. If it is disconnected, * or without associated Chardev, return 0. * + * Note that you are responsible to update the front-end sources if + * you are switching the main context with qemu_chr_fe_set_handlers(). + * * Returns: the source tag */ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, diff --git a/include/chardev/char-mux.h b/include/chardev/char-mux.h index 1e13187767..572cefd517 100644 --- a/include/chardev/char-mux.h +++ b/include/chardev/char-mux.h @@ -55,7 +55,6 @@ typedef struct MuxChardev { #define CHARDEV_IS_MUX(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) -void mux_chr_set_handlers(Chardev *chr, GMainContext *context); void mux_set_focus(Chardev *chr, int focus); void mux_chr_send_all_event(Chardev *chr, int event); diff --git a/include/chardev/char.h b/include/chardev/char.h index 014566c3de..c0b57f7685 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -73,6 +73,7 @@ struct Chardev { /** * qemu_chr_new_from_opts: * @opts: see qemu-config.c for a list of valid options + * @context: the #GMainContext to be used at initialization time * * Create a new character backend from a QemuOpts list. * @@ -81,6 +82,7 @@ struct Chardev { * or left untouched in case of help option */ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, + GMainContext *context, Error **errp); /** @@ -106,25 +108,29 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, * qemu_chr_new: * @label: the name of the backend * @filename: the URI + * @context: the #GMainContext to be used at initialization time * * Create a new character backend from a URI. * Do not implicitly initialize a monitor if the chardev is muxed. * * Returns: a new character backend */ -Chardev *qemu_chr_new(const char *label, const char *filename); +Chardev *qemu_chr_new(const char *label, const char *filename, + GMainContext *context); /** * qemu_chr_new_mux_mon: * @label: the name of the backend * @filename: the URI + * @context: the #GMainContext to be used at initialization time * * Create a new character backend from a URI. * Implicitly initialize a monitor if the chardev is muxed. * * Returns: a new character backend */ -Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename); +Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename, + GMainContext *context); /** * qemu_chr_change: @@ -146,6 +152,7 @@ void qemu_chr_cleanup(void); * @label: the name of the backend * @filename: the URI * @permit_mux_mon: if chardev is muxed, initialize a monitor + * @context: the #GMainContext to be used at initialization time * * Create a new character backend from a URI. * Character device communications are not written @@ -154,7 +161,7 @@ void qemu_chr_cleanup(void); * Returns: a new character backend */ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, - bool permit_mux_mon); + bool permit_mux_mon, GMainContext *context); /** * qemu_chr_be_can_write: @@ -272,7 +279,8 @@ typedef struct ChardevClass { } ChardevClass; Chardev *qemu_chardev_new(const char *id, const char *typename, - ChardevBackend *backend, Error **errp); + ChardevBackend *backend, GMainContext *context, + Error **errp); extern int term_escape_char; diff --git a/include/io/task.h b/include/io/task.h index 9e09b95b2e..57d8ba835e 100644 --- a/include/io/task.h +++ b/include/io/task.h @@ -232,7 +232,8 @@ QIOTask *qio_task_new(Object *source, * * Run a task in a background thread. When @worker * returns it will call qio_task_complete() in - * the event thread context that provided. + * the thread that is running the main loop associated + * with @context. */ void qio_task_run_in_thread(QIOTask *task, QIOTaskWorker worker, @@ -240,6 +241,32 @@ void qio_task_run_in_thread(QIOTask *task, GDestroyNotify destroy, GMainContext *context); + +/** + * qio_task_wait_thread: + * @task: the task struct + * + * Wait for completion of a task that was previously + * invoked using qio_task_run_in_thread. This MUST + * ONLY be invoked if the task has not already + * completed, since after the completion callback + * is invoked, @task will have been freed. + * + * To avoid racing with execution of the completion + * callback provided with qio_task_new, this method + * MUST ONLY be invoked from the thread that is + * running the main loop associated with @context + * parameter to qio_task_run_in_thread. + * + * When the thread has completed, the completion + * callback provided to qio_task_new will be invoked. + * When that callback returns @task will be freed, + * so @task must not be referenced after this + * method completes. + */ +void qio_task_wait_thread(QIOTask *task); + + /** * qio_task_complete: * @task: the task struct @@ -24,6 +24,15 @@ #include "qemu/thread.h" #include "trace.h" +struct QIOTaskThreadData { + QIOTaskWorker worker; + gpointer opaque; + GDestroyNotify destroy; + GMainContext *context; + GSource *completion; +}; + + struct QIOTask { Object *source; QIOTaskFunc func; @@ -32,6 +41,9 @@ struct QIOTask { Error *err; gpointer result; GDestroyNotify destroyResult; + QemuMutex thread_lock; + QemuCond thread_cond; + struct QIOTaskThreadData *thread; }; @@ -49,6 +61,8 @@ QIOTask *qio_task_new(Object *source, task->func = func; task->opaque = opaque; task->destroy = destroy; + qemu_mutex_init(&task->thread_lock); + qemu_cond_init(&task->thread_cond); trace_qio_task_new(task, source, func, opaque); @@ -57,6 +71,19 @@ QIOTask *qio_task_new(Object *source, static void qio_task_free(QIOTask *task) { + qemu_mutex_lock(&task->thread_lock); + if (task->thread) { + if (task->thread->destroy) { + task->thread->destroy(task->thread->opaque); + } + + if (task->thread->context) { + g_main_context_unref(task->thread->context); + } + + g_free(task->thread); + } + if (task->destroy) { task->destroy(task->opaque); } @@ -68,35 +95,20 @@ static void qio_task_free(QIOTask *task) } object_unref(task->source); + qemu_mutex_unlock(&task->thread_lock); + qemu_mutex_destroy(&task->thread_lock); + qemu_cond_destroy(&task->thread_cond); + g_free(task); } -struct QIOTaskThreadData { - QIOTask *task; - QIOTaskWorker worker; - gpointer opaque; - GDestroyNotify destroy; - GMainContext *context; -}; - - static gboolean qio_task_thread_result(gpointer opaque) { - struct QIOTaskThreadData *data = opaque; - - trace_qio_task_thread_result(data->task); - qio_task_complete(data->task); - - if (data->destroy) { - data->destroy(data->opaque); - } - - if (data->context) { - g_main_context_unref(data->context); - } + QIOTask *task = opaque; - g_free(data); + trace_qio_task_thread_result(task); + qio_task_complete(task); return FALSE; } @@ -104,22 +116,30 @@ static gboolean qio_task_thread_result(gpointer opaque) static gpointer qio_task_thread_worker(gpointer opaque) { - struct QIOTaskThreadData *data = opaque; - GSource *idle; + QIOTask *task = opaque; - trace_qio_task_thread_run(data->task); - data->worker(data->task, data->opaque); + trace_qio_task_thread_run(task); + + task->thread->worker(task, task->thread->opaque); /* We're running in the background thread, and must only * ever report the task results in the main event loop * thread. So we schedule an idle callback to report * the worker results */ - trace_qio_task_thread_exit(data->task); + trace_qio_task_thread_exit(task); + + qemu_mutex_lock(&task->thread_lock); - idle = g_idle_source_new(); - g_source_set_callback(idle, qio_task_thread_result, data, NULL); - g_source_attach(idle, data->context); + task->thread->completion = g_idle_source_new(); + g_source_set_callback(task->thread->completion, + qio_task_thread_result, task, NULL); + g_source_attach(task->thread->completion, + task->thread->context); + trace_qio_task_thread_source_attach(task, task->thread->completion); + + qemu_cond_signal(&task->thread_cond); + qemu_mutex_unlock(&task->thread_lock); return NULL; } @@ -138,21 +158,38 @@ void qio_task_run_in_thread(QIOTask *task, g_main_context_ref(context); } - data->task = task; data->worker = worker; data->opaque = opaque; data->destroy = destroy; data->context = context; + task->thread = data; + trace_qio_task_thread_start(task, worker, opaque); qemu_thread_create(&thread, "io-task-worker", qio_task_thread_worker, - data, + task, QEMU_THREAD_DETACHED); } +void qio_task_wait_thread(QIOTask *task) +{ + qemu_mutex_lock(&task->thread_lock); + g_assert(task->thread != NULL); + while (task->thread->completion == NULL) { + qemu_cond_wait(&task->thread_cond, &task->thread_lock); + } + + trace_qio_task_thread_source_cancel(task, task->thread->completion); + g_source_destroy(task->thread->completion); + qemu_mutex_unlock(&task->thread_lock); + + qio_task_thread_result(task); +} + + void qio_task_complete(QIOTask *task) { task->func(task, task->opaque); diff --git a/io/trace-events b/io/trace-events index f70bad7cbe..07a7bbec6a 100644 --- a/io/trace-events +++ b/io/trace-events @@ -7,6 +7,8 @@ qio_task_thread_start(void *task, void *worker, void *opaque) "Task thread start qio_task_thread_run(void *task) "Task thread run task=%p" qio_task_thread_exit(void *task) "Task thread exit task=%p" qio_task_thread_result(void *task) "Task thread result task=%p" +qio_task_thread_source_attach(void *task, void *source) "Task thread source attach task=%p source=%p" +qio_task_thread_source_cancel(void *task, void *source) "Task thread source cancel task=%p source=%p" # io/channel-socket.c qio_channel_socket_new(void *ioc) "Socket new ioc=%p" diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index f598d41891..83ecc6f799 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -83,7 +83,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) __put_user(env->fpr[i], &sc->fpr[i]); } - uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ + uint32_t fcsr = riscv_csr_read(env, CSR_FCSR); __put_user(fcsr, &sc->fcsr); } @@ -159,7 +159,7 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) uint32_t fcsr; __get_user(fcsr, &sc->fcsr); - csr_write_helper(env, fcsr, CSR_FCSR); + riscv_csr_write(env, CSR_FCSR, fcsr); } static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) diff --git a/net/slirp.c b/net/slirp.c index 7a16d8d615..4ec989b592 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -927,7 +927,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp) * FIXME: sure we want to support implicit * muxed monitors here? */ - Chardev *chr = qemu_chr_new_mux_mon(buf, p); + Chardev *chr = qemu_chr_new_mux_mon(buf, p, NULL); if (!chr) { error_setg(errp, "Could not open guest forwarding device '%s'", @@ -763,7 +763,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) { Chardev *chr; - chr = qemu_chr_new("qtest", qtest_chrdev); + chr = qemu_chr_new("qtest", qtest_chrdev, NULL); if (chr == NULL) { error_setg(errp, "Failed to initialize device for qtest: \"%s\"", diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index ce79c0b051..120108f582 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -114,7 +114,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) void icmp_detach(struct socket *so) { so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); - slirp_closesocket(so->s); + closesocket(so->s); sofree(so); } @@ -421,7 +421,7 @@ void icmp_receive(struct socket *so) icp = mtod(m, struct icmp *); id = icp->icmp_id; - len = slirp_recv(so->s, icp, M_ROOM(m), 0); + len = recv(so->s, icp, M_ROOM(m), 0); /* * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent * between host OSes. On Linux, only the ICMP header and payload is diff --git a/slirp/misc.c b/slirp/misc.c index 3f4cd852f8..d9fc586a24 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -98,16 +98,16 @@ slirp_socketpair_with_oob(int sv[2]) goto err; } - slirp_closesocket(s); + closesocket(s); return 0; err: g_critical("slirp_socketpair(): %s", strerror(errno)); if (s >= 0) { - slirp_closesocket(s); + closesocket(s); } if (sv[1] >= 0) { - slirp_closesocket(sv[1]); + closesocket(sv[1]); } return -1; } @@ -211,16 +211,16 @@ fork_exec(struct socket *so, const char *ex) if (err) { g_critical("fork_exec: %s", err->message); g_error_free(err); - slirp_closesocket(sp[0]); - slirp_closesocket(sp[1]); + closesocket(sp[0]); + closesocket(sp[1]); return 0; } so->s = sp[0]; - slirp_closesocket(sp[1]); + closesocket(sp[1]); slirp_socket_set_fast_reuse(so->s); opt = 1; - slirp_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); slirp_set_nonblock(so->s); so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); return 1; diff --git a/slirp/slirp.c b/slirp/slirp.c index a746c6fd86..55591430dc 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -961,7 +961,7 @@ int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, addr.sin_addr.s_addr == host_addr.s_addr && addr.sin_port == port) { so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); - slirp_closesocket(so->s); + closesocket(so->s); sofree(so); return 0; } diff --git a/slirp/socket.c b/slirp/socket.c index ce1d6ffa1d..4876ea3f31 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -185,7 +185,7 @@ soread(struct socket *so) */ sopreprbuf(so, iov, &n); - nn = slirp_recv(so->s, iov[0].iov_base, iov[0].iov_len,0); + nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); if (nn <= 0) { if (nn < 0 && (errno == EINTR || errno == EAGAIN)) return 0; @@ -201,7 +201,7 @@ soread(struct socket *so) if (getpeername(so->s, paddr, &alen) < 0) { err = errno; } else { - slirp_getsockopt(so->s, SOL_SOCKET, SO_ERROR, + getsockopt(so->s, SOL_SOCKET, SO_ERROR, &err, &elen); } } @@ -231,7 +231,7 @@ soread(struct socket *so) */ if (n == 2 && nn == iov[0].iov_len) { int ret; - ret = slirp_recv(so->s, iov[1].iov_base, iov[1].iov_len,0); + ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0); if (ret > 0) nn += ret; } @@ -552,7 +552,7 @@ sorecvfrom(struct socket *so) */ len = M_FREEROOM(m); /* if (so->so_fport != htons(53)) { */ - slirp_ioctlsocket(so->s, FIONREAD, &n); + ioctlsocket(so->s, FIONREAD, &n); if (n > len) { n = (m->m_data - m->m_dat) + m->m_len + n + 1; @@ -724,7 +724,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, uint32_t laddr, int tmperrno = errno; /* Don't clobber the real reason we failed */ if (s >= 0) { - slirp_closesocket(s); + closesocket(s); } sofree(so); /* Restore the real errno */ @@ -735,9 +735,9 @@ tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, uint32_t laddr, #endif return NULL; } - slirp_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); opt = 1; - slirp_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); so->so_ffamily = AF_INET; diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index cda94815f6..262a42d6c8 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -337,7 +337,7 @@ tcp_close(struct tcpcb *tp) if (so == slirp->tcp_last_so) slirp->tcp_last_so = &slirp->tcb; so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); - slirp_closesocket(so->s); + closesocket(so->s); sbfree(&so->so_rcv); sbfree(&so->so_snd); sofree(so); @@ -416,9 +416,9 @@ int tcp_fconnect(struct socket *so, unsigned short af) so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); slirp_socket_set_fast_reuse(s); opt = 1; - slirp_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); opt = 1; - slirp_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); addr = so->fhost.ss; DEBUG_CALL(" connect()ing"); @@ -489,7 +489,7 @@ void tcp_connect(struct socket *inso) so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); slirp_socket_set_fast_reuse(s); opt = 1; - slirp_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); slirp_socket_set_nodelay(s); so->fhost.ss = addr; @@ -499,7 +499,7 @@ void tcp_connect(struct socket *inso) if (inso->so_state & SS_FACCEPTONCE) { /* If we only accept once, close the accept() socket */ so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); - slirp_closesocket(so->s); + closesocket(so->s); /* Don't select it yet, even though we have an FD */ /* if it's not FACCEPTONCE, it's already NOFDREF */ diff --git a/slirp/udp.c b/slirp/udp.c index 29a31e9400..3d9a19b85a 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -292,7 +292,7 @@ void udp_detach(struct socket *so) { so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); - slirp_closesocket(so->s); + closesocket(so->s); sofree(so); } diff --git a/slirp/util.c b/slirp/util.c index 84f5afdbc3..1cbaa26b60 100644 --- a/slirp/util.c +++ b/slirp/util.c @@ -167,7 +167,7 @@ static int socket_error(void) } #undef ioctlsocket -int slirp_ioctlsocket(int fd, int req, void *val) +int slirp_ioctlsocket_wrap(int fd, int req, void *val) { int ret; ret = ioctlsocket(fd, req, val); @@ -178,7 +178,7 @@ int slirp_ioctlsocket(int fd, int req, void *val) } #undef closesocket -int slirp_closesocket(int fd) +int slirp_closesocket_wrap(int fd) { int ret; ret = closesocket(fd); @@ -187,6 +187,166 @@ int slirp_closesocket(int fd) } return ret; } + +#undef connect +int slirp_connect_wrap(int sockfd, const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = connect(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef listen +int slirp_listen_wrap(int sockfd, int backlog) +{ + int ret; + ret = listen(sockfd, backlog); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef bind +int slirp_bind_wrap(int sockfd, const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = bind(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef socket +int slirp_socket_wrap(int domain, int type, int protocol) +{ + int ret; + ret = socket(domain, type, protocol); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef accept +int slirp_accept_wrap(int sockfd, struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = accept(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef shutdown +int slirp_shutdown_wrap(int sockfd, int how) +{ + int ret; + ret = shutdown(sockfd, how); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef getsockopt +int slirp_getsockopt_wrap(int sockfd, int level, int optname, + void *optval, int *optlen) +{ + int ret; + ret = getsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef setsockopt +int slirp_setsockopt_wrap(int sockfd, int level, int optname, + const void *optval, int optlen) +{ + int ret; + ret = setsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef getpeername +int slirp_getpeername_wrap(int sockfd, struct sockaddr *addr, + int *addrlen) +{ + int ret; + ret = getpeername(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef getsockname +int slirp_getsockname_wrap(int sockfd, struct sockaddr *addr, + int *addrlen) +{ + int ret; + ret = getsockname(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef send +ssize_t slirp_send_wrap(int sockfd, const void *buf, size_t len, int flags) +{ + int ret; + ret = send(sockfd, buf, len, flags); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef sendto +ssize_t slirp_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = sendto(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef recv +ssize_t slirp_recv_wrap(int sockfd, void *buf, size_t len, int flags) +{ + int ret; + ret = recv(sockfd, buf, len, flags); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef recvfrom +ssize_t slirp_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} #endif /* WIN32 */ void slirp_pstrcpy(char *buf, int buf_size, const char *str) diff --git a/slirp/util.h b/slirp/util.h index 4963747aef..c4207a49d6 100644 --- a/slirp/util.h +++ b/slirp/util.h @@ -81,21 +81,68 @@ struct iovec { #define ETH_P_NCSI (0x88f8) #define ETH_P_UNKNOWN (0xffff) +/* FIXME: remove me when made standalone */ #ifdef _WIN32 -int slirp_closesocket(int fd); -int slirp_ioctlsocket(int fd, int req, void *val); +#undef accept +#undef bind +#undef closesocket +#undef connect +#undef getpeername +#undef getsockname +#undef getsockopt +#undef ioctlsocket +#undef listen +#undef recv +#undef recvfrom +#undef send +#undef sendto +#undef setsockopt +#undef shutdown +#undef socket +#endif + +#ifdef _WIN32 +#define connect slirp_connect_wrap +int slirp_connect_wrap(int fd, const struct sockaddr *addr, int addrlen); +#define listen slirp_listen_wrap +int slirp_listen_wrap(int fd, int backlog); +#define bind slirp_bind_wrap +int slirp_bind_wrap(int fd, const struct sockaddr *addr, int addrlen); +#define socket slirp_socket_wrap +int slirp_socket_wrap(int domain, int type, int protocol); +#define accept slirp_accept_wrap +int slirp_accept_wrap(int fd, struct sockaddr *addr, int *addrlen); +#define shutdown slirp_shutdown_wrap +int slirp_shutdown_wrap(int fd, int how); +#define getpeername slirp_getpeername_wrap +int slirp_getpeername_wrap(int fd, struct sockaddr *addr, int *addrlen); +#define getsockname slirp_getsockname_wrap +int slirp_getsockname_wrap(int fd, struct sockaddr *addr, int *addrlen); +#define send slirp_send_wrap +ssize_t slirp_send_wrap(int fd, const void *buf, size_t len, int flags); +#define sendto slirp_sendto_wrap +ssize_t slirp_sendto_wrap(int fd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, int addrlen); +#define recv slirp_recv_wrap +ssize_t slirp_recv_wrap(int fd, void *buf, size_t len, int flags); +#define recvfrom slirp_recvfrom_wrap +ssize_t slirp_recvfrom_wrap(int fd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, int *addrlen); +#define closesocket slirp_closesocket_wrap +int slirp_closesocket_wrap(int fd); +#define ioctlsocket slirp_ioctlsocket_wrap +int slirp_ioctlsocket_wrap(int fd, int req, void *val); +#define getsockopt slirp_getsockopt_wrap +int slirp_getsockopt_wrap(int sockfd, int level, int optname, + void *optval, int *optlen); +#define setsockopt slirp_setsockopt_wrap +int slirp_setsockopt_wrap(int sockfd, int level, int optname, + const void *optval, int optlen); + int inet_aton(const char *cp, struct in_addr *ia); -#define slirp_getsockopt(sockfd, level, optname, optval, optlen) \ - getsockopt(sockfd, level, optname, (void *)optval, optlen) -#define slirp_setsockopt(sockfd, level, optname, optval, optlen) \ - setsockopt(sockfd, level, optname, (const void *)optval, optlen) -#define slirp_recv(sockfd, buf, len, flags) recv(sockfd, (void *)buf, len, flags) #else -#define slirp_setsockopt setsockopt -#define slirp_getsockopt getsockopt -#define slirp_recv recv -#define slirp_closesocket close -#define slirp_ioctlsocket ioctl +#define closesocket(s) close(s) +#define ioctlsocket(s, r, v) ioctl(s, r, v) #endif int slirp_socket(int domain, int type, int protocol); @@ -104,14 +151,14 @@ void slirp_set_nonblock(int fd); static inline int slirp_socket_set_nodelay(int fd) { int v = 1; - return slirp_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); } static inline int slirp_socket_set_fast_reuse(int fd) { #ifndef _WIN32 int v = 1; - return slirp_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); #else /* Enabling the reuse of an endpoint that was used by a socket still in * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows diff --git a/target/hppa/Makefile.objs b/target/hppa/Makefile.objs index 3359da5341..174f50a96c 100644 --- a/target/hppa/Makefile.objs +++ b/target/hppa/Makefile.objs @@ -1,3 +1,11 @@ obj-y += translate.o helper.o cpu.o op_helper.o gdbstub.o mem_helper.o obj-y += int_helper.o obj-$(CONFIG_SOFTMMU) += machine.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/hppa/decode.inc.c: $(SRC_PATH)/target/hppa/insns.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@) + +target/hppa/translate.o: target/hppa/decode.inc.c diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode new file mode 100644 index 0000000000..55ff39dd05 --- /dev/null +++ b/target/hppa/insns.decode @@ -0,0 +1,527 @@ +# +# HPPA instruction decode definitions. +# +# Copyright (c) 2018 Richard Henderson <rth@twiddle.net> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see <http://www.gnu.org/licenses/>. +# + +#### +# Field definitions +#### + +%assemble_sr3 13:1 14:2 +%assemble_sr3x 13:1 14:2 !function=expand_sr3x + +%assemble_11a 0:s1 4:10 !function=expand_shl3 +%assemble_12 0:s1 2:1 3:10 !function=expand_shl2 +%assemble_12a 0:s1 3:11 !function=expand_shl2 +%assemble_17 0:s1 16:5 2:1 3:10 !function=expand_shl2 +%assemble_22 0:s1 16:10 2:1 3:10 !function=expand_shl2 + +%assemble_21 0:s1 1:11 14:2 16:5 12:2 !function=expand_shl11 + +%lowsign_11 0:s1 1:10 +%lowsign_14 0:s1 1:13 + +%sm_imm 16:10 !function=expand_sm_imm + +%rm64 1:1 16:5 +%rt64 6:1 0:5 +%ra64 7:1 21:5 +%rb64 12:1 16:5 +%rc64 8:1 13:3 9:2 +%rc32 13:3 9:2 + +%im5_0 0:s1 1:4 +%im5_16 16:s1 17:4 +%ma_to_m 5:1 13:1 !function=ma_to_m +%ma2_to_m 2:2 !function=ma_to_m +%pos_to_m 0:1 !function=pos_to_m +%neg_to_m 0:1 !function=neg_to_m +%a_to_m 2:1 !function=neg_to_m + +#### +# Argument set definitions +#### + +# All insns that need to form a virtual address should use this set. +&ldst t b x disp sp m scale size + +&rr_cf t r cf +&rrr_cf t r1 r2 cf +&rrr_cf_sh t r1 r2 cf sh +&rri_cf t r i cf + +&rrb_c_f disp n c f r1 r2 +&rib_c_f disp n c f r i + +#### +# Format definitions +#### + +@rr_cf ...... r:5 ..... cf:4 ....... t:5 &rr_cf +@rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf +@rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh +@rrr_cf_sh0 ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf_sh sh=0 +@rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 + +@rrb_cf ...... r2:5 r1:5 c:3 ........... n:1 . \ + &rrb_c_f disp=%assemble_12 +@rib_cf ...... r:5 ..... c:3 ........... n:1 . \ + &rib_c_f disp=%assemble_12 i=%im5_16 + +#### +# System +#### + +break 000000 ----- ----- --- 00000000 ----- + +mtsp 000000 ----- r:5 ... 11000001 00000 sp=%assemble_sr3 +mtctl 000000 t:5 r:5 --- 11000010 00000 +mtsarcm 000000 01011 r:5 --- 11000110 00000 +mtsm 000000 00000 r:5 000 11000011 00000 + +mfia 000000 ----- 00000 --- 10100101 t:5 +mfsp 000000 ----- 00000 ... 00100101 t:5 sp=%assemble_sr3 +mfctl 000000 r:5 00000- e:1 -01000101 t:5 + +sync 000000 ----- ----- 000 00100000 00000 # sync, syncdma + +ldsid 000000 b:5 ----- sp:2 0 10000101 t:5 + +rsm 000000 .......... 000 01110011 t:5 i=%sm_imm +ssm 000000 .......... 000 01101011 t:5 i=%sm_imm + +rfi 000000 ----- ----- --- 01100000 00000 +rfi_r 000000 ----- ----- --- 01100101 00000 + +# These are artificial instructions used by QEMU firmware. +# They are allocated from the unassigned instruction space. +halt 1111 1111 1111 1101 1110 1010 1101 0000 +reset 1111 1111 1111 1101 1110 1010 1101 0001 + +#### +# Memory Management +#### + +@addrx ...... b:5 x:5 .. ........ m:1 ..... \ + &ldst disp=0 scale=0 t=0 sp=0 size=0 + +nop 000001 ----- ----- -- 11001010 0 ----- # fdc, disp +nop_addrx 000001 ..... ..... -- 01001010 . ----- @addrx # fdc, index +nop_addrx 000001 ..... ..... -- 01001011 . ----- @addrx # fdce +nop_addrx 000001 ..... ..... --- 0001010 . ----- @addrx # fic 0x0a +nop_addrx 000001 ..... ..... -- 01001111 . 00000 @addrx # fic 0x4f +nop_addrx 000001 ..... ..... --- 0001011 . ----- @addrx # fice +nop_addrx 000001 ..... ..... -- 01001110 . 00000 @addrx # pdc + +probe 000001 b:5 ri:5 sp:2 imm:1 100011 write:1 0 t:5 + +ixtlbx 000001 b:5 r:5 sp:2 0100000 addr:1 0 00000 data=1 +ixtlbx 000001 b:5 r:5 ... 000000 addr:1 0 00000 \ + sp=%assemble_sr3x data=0 + +pxtlbx 000001 b:5 x:5 sp:2 0100100 local:1 m:1 ----- data=1 +pxtlbx 000001 b:5 x:5 ... 000100 local:1 m:1 ----- \ + sp=%assemble_sr3x data=0 + +lpa 000001 b:5 x:5 sp:2 01001101 m:1 t:5 \ + &ldst disp=0 scale=0 size=0 + +lci 000001 ----- ----- -- 01001100 0 t:5 + +#### +# Arith/Log +#### + +andcm 000010 ..... ..... .... 000000 0 ..... @rrr_cf +and 000010 ..... ..... .... 001000 0 ..... @rrr_cf +or 000010 ..... ..... .... 001001 0 ..... @rrr_cf +xor 000010 ..... ..... .... 001010 0 ..... @rrr_cf +uxor 000010 ..... ..... .... 001110 0 ..... @rrr_cf +ds 000010 ..... ..... .... 010001 0 ..... @rrr_cf +cmpclr 000010 ..... ..... .... 100010 0 ..... @rrr_cf +uaddcm 000010 ..... ..... .... 100110 0 ..... @rrr_cf +uaddcm_tc 000010 ..... ..... .... 100111 0 ..... @rrr_cf +dcor 000010 ..... 00000 .... 101110 0 ..... @rr_cf +dcor_i 000010 ..... 00000 .... 101111 0 ..... @rr_cf + +add 000010 ..... ..... .... 0110.. 0 ..... @rrr_cf_sh +add_l 000010 ..... ..... .... 1010.. 0 ..... @rrr_cf_sh +add_tsv 000010 ..... ..... .... 1110.. 0 ..... @rrr_cf_sh +add_c 000010 ..... ..... .... 011100 0 ..... @rrr_cf_sh0 +add_c_tsv 000010 ..... ..... .... 111100 0 ..... @rrr_cf_sh0 + +sub 000010 ..... ..... .... 010000 0 ..... @rrr_cf +sub_tsv 000010 ..... ..... .... 110000 0 ..... @rrr_cf +sub_tc 000010 ..... ..... .... 010011 0 ..... @rrr_cf +sub_tsv_tc 000010 ..... ..... .... 110011 0 ..... @rrr_cf +sub_b 000010 ..... ..... .... 010100 0 ..... @rrr_cf +sub_b_tsv 000010 ..... ..... .... 110100 0 ..... @rrr_cf + +ldil 001000 t:5 ..................... i=%assemble_21 +addil 001010 r:5 ..................... i=%assemble_21 +ldo 001101 b:5 t:5 -- .............. i=%lowsign_14 + +addi 101101 ..... ..... .... 0 ........... @rri_cf +addi_tsv 101101 ..... ..... .... 1 ........... @rri_cf +addi_tc 101100 ..... ..... .... 0 ........... @rri_cf +addi_tc_tsv 101100 ..... ..... .... 1 ........... @rri_cf + +subi 100101 ..... ..... .... 0 ........... @rri_cf +subi_tsv 100101 ..... ..... .... 1 ........... @rri_cf + +cmpiclr 100100 ..... ..... .... 0 ........... @rri_cf + +#### +# Index Mem +#### + +@ldstx ...... b:5 x:5 sp:2 scale:1 ....... m:1 t:5 &ldst disp=0 +@ldim5 ...... b:5 ..... sp:2 ......... t:5 \ + &ldst disp=%im5_16 x=0 scale=0 m=%ma_to_m +@stim5 ...... b:5 t:5 sp:2 ......... ..... \ + &ldst disp=%im5_0 x=0 scale=0 m=%ma_to_m + +ld 000011 ..... ..... .. . 1 -- 00 size:2 ...... @ldim5 +ld 000011 ..... ..... .. . 0 -- 00 size:2 ...... @ldstx +st 000011 ..... ..... .. . 1 -- 10 size:2 ...... @stim5 +ldc 000011 ..... ..... .. . 1 -- 0111 ...... @ldim5 size=2 +ldc 000011 ..... ..... .. . 0 -- 0111 ...... @ldstx size=2 +lda 000011 ..... ..... .. . 1 -- 0110 ...... @ldim5 size=2 +lda 000011 ..... ..... .. . 0 -- 0110 ...... @ldstx size=2 +sta 000011 ..... ..... .. . 1 -- 1110 ...... @stim5 size=2 +stby 000011 b:5 r:5 sp:2 a:1 1 -- 1100 m:1 ..... disp=%im5_0 + +@fldstwx ...... b:5 x:5 sp:2 scale:1 ....... m:1 ..... \ + &ldst t=%rt64 disp=0 size=2 +@fldstwi ...... b:5 ..... sp:2 . ....... . ..... \ + &ldst t=%rt64 disp=%im5_16 m=%ma_to_m x=0 scale=0 size=2 + +fldw 001001 ..... ..... .. . 0 -- 000 . . ..... @fldstwx +fldw 001001 ..... ..... .. . 1 -- 000 . . ..... @fldstwi +fstw 001001 ..... ..... .. . 0 -- 100 . . ..... @fldstwx +fstw 001001 ..... ..... .. . 1 -- 100 . . ..... @fldstwi + +@fldstdx ...... b:5 x:5 sp:2 scale:1 ....... m:1 t:5 \ + &ldst disp=0 size=3 +@fldstdi ...... b:5 ..... sp:2 . ....... . t:5 \ + &ldst disp=%im5_16 m=%ma_to_m x=0 scale=0 size=3 + +fldd 001011 ..... ..... .. . 0 -- 000 0 . ..... @fldstdx +fldd 001011 ..... ..... .. . 1 -- 000 0 . ..... @fldstdi +fstd 001011 ..... ..... .. . 0 -- 100 0 . ..... @fldstdx +fstd 001011 ..... ..... .. . 1 -- 100 0 . ..... @fldstdi + +#### +# Offset Mem +#### + +@ldstim14 ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%lowsign_14 x=0 scale=0 m=0 +@ldstim14m ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%lowsign_14 x=0 scale=0 m=%neg_to_m +@ldstim12m ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%assemble_12a x=0 scale=0 m=%pos_to_m + +# LDB, LDH, LDW, LDWM +ld 010000 ..... ..... .. .............. @ldstim14 size=0 +ld 010001 ..... ..... .. .............. @ldstim14 size=1 +ld 010010 ..... ..... .. .............. @ldstim14 size=2 +ld 010011 ..... ..... .. .............. @ldstim14m size=2 +ld 010111 ..... ..... .. ...........10. @ldstim12m size=2 + +# STB, STH, STW, STWM +st 011000 ..... ..... .. .............. @ldstim14 size=0 +st 011001 ..... ..... .. .............. @ldstim14 size=1 +st 011010 ..... ..... .. .............. @ldstim14 size=2 +st 011011 ..... ..... .. .............. @ldstim14m size=2 +st 011111 ..... ..... .. ...........10. @ldstim12m size=2 + +fldw 010110 b:5 ..... sp:2 .............. \ + &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2 +fldw 010111 b:5 ..... sp:2 ...........0.. \ + &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 + +fstw 011110 b:5 ..... sp:2 .............. \ + &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2 +fstw 011111 b:5 ..... sp:2 ...........0.. \ + &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 + +fldd 010100 b:5 t:5 sp:2 .......... .. 1 . \ + &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 + +fstd 011100 b:5 t:5 sp:2 .......... .. 1 . \ + &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 + +#### +# Floating-point Multiply Add +#### + +&mpyadd rm1 rm2 ta ra tm +@mpyadd ...... rm1:5 rm2:5 ta:5 ra:5 . tm:5 &mpyadd + +fmpyadd_f 000110 ..... ..... ..... ..... 0 ..... @mpyadd +fmpyadd_d 000110 ..... ..... ..... ..... 1 ..... @mpyadd +fmpysub_f 100110 ..... ..... ..... ..... 0 ..... @mpyadd +fmpysub_d 100110 ..... ..... ..... ..... 1 ..... @mpyadd + +#### +# Conditional Branches +#### + +bb_sar 110000 00000 r:5 c:1 10 ........... n:1 . disp=%assemble_12 +bb_imm 110001 p:5 r:5 c:1 10 ........... n:1 . disp=%assemble_12 + +movb 110010 ..... ..... ... ........... . . @rrb_cf f=0 +movbi 110011 ..... ..... ... ........... . . @rib_cf f=0 + +cmpb 100000 ..... ..... ... ........... . . @rrb_cf f=0 +cmpb 100010 ..... ..... ... ........... . . @rrb_cf f=1 +cmpbi 100001 ..... ..... ... ........... . . @rib_cf f=0 +cmpbi 100011 ..... ..... ... ........... . . @rib_cf f=1 + +addb 101000 ..... ..... ... ........... . . @rrb_cf f=0 +addb 101010 ..... ..... ... ........... . . @rrb_cf f=1 +addbi 101001 ..... ..... ... ........... . . @rib_cf f=0 +addbi 101011 ..... ..... ... ........... . . @rib_cf f=1 + +#### +# Shift, Extract, Deposit +#### + +shrpw_sar 110100 r2:5 r1:5 c:3 00 0 00000 t:5 +shrpw_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 + +extrw_sar 110100 r:5 t:5 c:3 10 se:1 00000 clen:5 +extrw_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 clen:5 + +depw_sar 110101 t:5 r:5 c:3 00 nz:1 00000 clen:5 +depw_imm 110101 t:5 r:5 c:3 01 nz:1 cpos:5 clen:5 +depwi_sar 110101 t:5 ..... c:3 10 nz:1 00000 clen:5 i=%im5_16 +depwi_imm 110101 t:5 ..... c:3 11 nz:1 cpos:5 clen:5 i=%im5_16 + +#### +# Branch External +#### + +&BE b l n disp sp +@be ...... b:5 ..... ... ........... n:1 . \ + &BE disp=%assemble_17 sp=%assemble_sr3 + +be 111000 ..... ..... ... ........... . . @be l=0 +be 111001 ..... ..... ... ........... . . @be l=31 + +#### +# Branch +#### + +&BL l n disp +@bl ...... l:5 ..... ... ........... n:1 . &BL disp=%assemble_17 + +# B,L and B,L,PUSH +bl 111010 ..... ..... 000 ........... . . @bl +bl 111010 ..... ..... 100 ........... . . @bl +# B,L (long displacement) +bl 111010 ..... ..... 101 ........... n:1 . &BL l=2 \ + disp=%assemble_22 +b_gate 111010 ..... ..... 001 ........... . . @bl +blr 111010 l:5 x:5 010 00000000000 n:1 0 +bv 111010 b:5 x:5 110 00000000000 n:1 0 +bve 111010 b:5 00000 110 10000000000 n:1 - l=0 +bve 111010 b:5 00000 111 10000000000 n:1 - l=2 + +#### +# FP Fused Multiple-Add +#### + +fmpyfadd_f 101110 ..... ..... ... . 0 ... . . neg:1 ..... \ + rm1=%ra64 rm2=%rb64 ra3=%rc64 t=%rt64 +fmpyfadd_d 101110 rm1:5 rm2:5 ... 0 1 ..0 0 0 neg:1 t:5 ra3=%rc32 + +#### +# FP operations +#### + +&fclass01 r t +&fclass2 r1 r2 c y +&fclass3 r1 r2 t + +@f0c_0 ...... r:5 00000 ..... 00 000 0 t:5 &fclass01 +@f0c_1 ...... r:5 000.. ..... 01 000 0 t:5 &fclass01 +@f0c_2 ...... r1:5 r2:5 y:3 .. 10 000 . c:5 &fclass2 +@f0c_3 ...... r1:5 r2:5 ..... 11 000 0 t:5 &fclass3 + +@f0e_f_0 ...... ..... 00000 ... 0 0 000 .. 0 ..... \ + &fclass01 r=%ra64 t=%rt64 +@f0e_d_0 ...... r:5 00000 ... 0 1 000 00 0 t:5 &fclass01 + +@f0e_ff_1 ...... ..... 000 ... 0000 010 .. 0 ..... \ + &fclass01 r=%ra64 t=%rt64 +@f0e_fd_1 ...... ..... 000 ... 0100 010 .0 0 t:5 &fclass01 r=%ra64 +@f0e_df_1 ...... r:5 000 ... 0001 010 0. 0 ..... &fclass01 t=%rt64 +@f0e_dd_1 ...... r:5 000 ... 0101 010 00 0 t:5 &fclass01 + +@f0e_f_2 ...... ..... ..... y:3 .0 100 .00 c:5 \ + &fclass2 r1=%ra64 r2=%rb64 +@f0e_d_2 ...... r1:5 r2:5 y:3 01 100 000 c:5 &fclass2 + +@f0e_f_3 ...... ..... ..... ... .0 110 ..0 ..... \ + &fclass3 r1=%ra64 r2=%rb64 t=%rt64 +@f0e_d_3 ...... r1:5 r2:5 ... 01 110 000 t:5 + +# Floating point class 0 + +# FID. With r = t = 0, which via fcpy puts 0 into fr0. +# This is machine/revision = 0, which is reserved for simulator. +fcpy_f 001100 00000 00000 00000 000000 00000 \ + &fclass01 r=0 t=0 + +fcpy_f 001100 ..... ..... 010 00 ...... ..... @f0c_0 +fabs_f 001100 ..... ..... 011 00 ...... ..... @f0c_0 +fsqrt_f 001100 ..... ..... 100 00 ...... ..... @f0c_0 +frnd_f 001100 ..... ..... 101 00 ...... ..... @f0c_0 +fneg_f 001100 ..... ..... 110 00 ...... ..... @f0c_0 +fnegabs_f 001100 ..... ..... 111 00 ...... ..... @f0c_0 + +fcpy_d 001100 ..... ..... 010 01 ...... ..... @f0c_0 +fabs_d 001100 ..... ..... 011 01 ...... ..... @f0c_0 +fsqrt_d 001100 ..... ..... 100 01 ...... ..... @f0c_0 +frnd_d 001100 ..... ..... 101 01 ...... ..... @f0c_0 +fneg_d 001100 ..... ..... 110 01 ...... ..... @f0c_0 +fnegabs_d 001100 ..... ..... 111 01 ...... ..... @f0c_0 + +fcpy_f 001110 ..... ..... 010 ........ ..... @f0e_f_0 +fabs_f 001110 ..... ..... 011 ........ ..... @f0e_f_0 +fsqrt_f 001110 ..... ..... 100 ........ ..... @f0e_f_0 +frnd_f 001110 ..... ..... 101 ........ ..... @f0e_f_0 +fneg_f 001110 ..... ..... 110 ........ ..... @f0e_f_0 +fnegabs_f 001110 ..... ..... 111 ........ ..... @f0e_f_0 + +fcpy_d 001110 ..... ..... 010 ........ ..... @f0e_d_0 +fabs_d 001110 ..... ..... 011 ........ ..... @f0e_d_0 +fsqrt_d 001110 ..... ..... 100 ........ ..... @f0e_d_0 +frnd_d 001110 ..... ..... 101 ........ ..... @f0e_d_0 +fneg_d 001110 ..... ..... 110 ........ ..... @f0e_d_0 +fnegabs_d 001110 ..... ..... 111 ........ ..... @f0e_d_0 + +# Floating point class 1 + +# float/float +fcnv_d_f 001100 ..... ... 000 00 01 ...... ..... @f0c_1 +fcnv_f_d 001100 ..... ... 000 01 00 ...... ..... @f0c_1 + +fcnv_d_f 001110 ..... ... 000 .......... ..... @f0e_df_1 +fcnv_f_d 001110 ..... ... 000 .......... ..... @f0e_fd_1 + +# int/float +fcnv_w_f 001100 ..... ... 001 00 00 ...... ..... @f0c_1 +fcnv_q_f 001100 ..... ... 001 00 01 ...... ..... @f0c_1 +fcnv_w_d 001100 ..... ... 001 01 00 ...... ..... @f0c_1 +fcnv_q_d 001100 ..... ... 001 01 01 ...... ..... @f0c_1 + +fcnv_w_f 001110 ..... ... 001 .......... ..... @f0e_ff_1 +fcnv_q_f 001110 ..... ... 001 .......... ..... @f0e_df_1 +fcnv_w_d 001110 ..... ... 001 .......... ..... @f0e_fd_1 +fcnv_q_d 001110 ..... ... 001 .......... ..... @f0e_dd_1 + +# float/int +fcnv_f_w 001100 ..... ... 010 00 00 ...... ..... @f0c_1 +fcnv_d_w 001100 ..... ... 010 00 01 ...... ..... @f0c_1 +fcnv_f_q 001100 ..... ... 010 01 00 ...... ..... @f0c_1 +fcnv_d_q 001100 ..... ... 010 01 01 ...... ..... @f0c_1 + +fcnv_f_w 001110 ..... ... 010 .......... ..... @f0e_ff_1 +fcnv_d_w 001110 ..... ... 010 .......... ..... @f0e_df_1 +fcnv_f_q 001110 ..... ... 010 .......... ..... @f0e_fd_1 +fcnv_d_q 001110 ..... ... 010 .......... ..... @f0e_dd_1 + +# float/int truncate +fcnv_t_f_w 001100 ..... ... 011 00 00 ...... ..... @f0c_1 +fcnv_t_d_w 001100 ..... ... 011 00 01 ...... ..... @f0c_1 +fcnv_t_f_q 001100 ..... ... 011 01 00 ...... ..... @f0c_1 +fcnv_t_d_q 001100 ..... ... 011 01 01 ...... ..... @f0c_1 + +fcnv_t_f_w 001110 ..... ... 011 .......... ..... @f0e_ff_1 +fcnv_t_d_w 001110 ..... ... 011 .......... ..... @f0e_df_1 +fcnv_t_f_q 001110 ..... ... 011 .......... ..... @f0e_fd_1 +fcnv_t_d_q 001110 ..... ... 011 .......... ..... @f0e_dd_1 + +# uint/float +fcnv_uw_f 001100 ..... ... 101 00 00 ...... ..... @f0c_1 +fcnv_uq_f 001100 ..... ... 101 00 01 ...... ..... @f0c_1 +fcnv_uw_d 001100 ..... ... 101 01 00 ...... ..... @f0c_1 +fcnv_uq_d 001100 ..... ... 101 01 01 ...... ..... @f0c_1 + +fcnv_uw_f 001110 ..... ... 101 .......... ..... @f0e_ff_1 +fcnv_uq_f 001110 ..... ... 101 .......... ..... @f0e_df_1 +fcnv_uw_d 001110 ..... ... 101 .......... ..... @f0e_fd_1 +fcnv_uq_d 001110 ..... ... 101 .......... ..... @f0e_dd_1 + +# float/int +fcnv_f_uw 001100 ..... ... 110 00 00 ...... ..... @f0c_1 +fcnv_d_uw 001100 ..... ... 110 00 01 ...... ..... @f0c_1 +fcnv_f_uq 001100 ..... ... 110 01 00 ...... ..... @f0c_1 +fcnv_d_uq 001100 ..... ... 110 01 01 ...... ..... @f0c_1 + +fcnv_f_uw 001110 ..... ... 110 .......... ..... @f0e_ff_1 +fcnv_d_uw 001110 ..... ... 110 .......... ..... @f0e_df_1 +fcnv_f_uq 001110 ..... ... 110 .......... ..... @f0e_fd_1 +fcnv_d_uq 001110 ..... ... 110 .......... ..... @f0e_dd_1 + +# float/int truncate +fcnv_t_f_uw 001100 ..... ... 111 00 00 ...... ..... @f0c_1 +fcnv_t_d_uw 001100 ..... ... 111 00 01 ...... ..... @f0c_1 +fcnv_t_f_uq 001100 ..... ... 111 01 00 ...... ..... @f0c_1 +fcnv_t_d_uq 001100 ..... ... 111 01 01 ...... ..... @f0c_1 + +fcnv_t_f_uw 001110 ..... ... 111 .......... ..... @f0e_ff_1 +fcnv_t_d_uw 001110 ..... ... 111 .......... ..... @f0e_df_1 +fcnv_t_f_uq 001110 ..... ... 111 .......... ..... @f0e_fd_1 +fcnv_t_d_uq 001110 ..... ... 111 .......... ..... @f0e_dd_1 + +# Floating point class 2 + +ftest 001100 00000 00000 y:3 00 10000 1 c:5 + +fcmp_f 001100 ..... ..... ... 00 ..... 0 ..... @f0c_2 +fcmp_d 001100 ..... ..... ... 01 ..... 0 ..... @f0c_2 + +fcmp_f 001110 ..... ..... ... ..... ... ..... @f0e_f_2 +fcmp_d 001110 ..... ..... ... ..... ... ..... @f0e_d_2 + +# Floating point class 3 + +fadd_f 001100 ..... ..... 000 00 ...... ..... @f0c_3 +fsub_f 001100 ..... ..... 001 00 ...... ..... @f0c_3 +fmpy_f 001100 ..... ..... 010 00 ...... ..... @f0c_3 +fdiv_f 001100 ..... ..... 011 00 ...... ..... @f0c_3 + +fadd_d 001100 ..... ..... 000 01 ...... ..... @f0c_3 +fsub_d 001100 ..... ..... 001 01 ...... ..... @f0c_3 +fmpy_d 001100 ..... ..... 010 01 ...... ..... @f0c_3 +fdiv_d 001100 ..... ..... 011 01 ...... ..... @f0c_3 + +fadd_f 001110 ..... ..... 000 ..... ... ..... @f0e_f_3 +fsub_f 001110 ..... ..... 001 ..... ... ..... @f0e_f_3 +fmpy_f 001110 ..... ..... 010 ..... ... ..... @f0e_f_3 +fdiv_f 001110 ..... ..... 011 ..... ... ..... @f0e_f_3 + +fadd_d 001110 ..... ..... 000 ..... ... ..... @f0e_d_3 +fsub_d 001110 ..... ..... 001 ..... ... ..... @f0e_d_3 +fmpy_d 001110 ..... ..... 010 ..... ... ..... @f0e_d_3 +fdiv_d 001110 ..... ..... 011 ..... ... ..... @f0e_d_3 + +xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64 diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 6bf478e7b0..268caaaa20 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -81,10 +81,8 @@ static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val, } static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, - bool parallel) + bool parallel, uintptr_t ra) { - uintptr_t ra = GETPC(); - switch (addr & 3) { case 3: cpu_stb_data_ra(env, addr, val, ra); @@ -109,20 +107,18 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_b(env, addr, val, false); + do_stby_b(env, addr, val, false, GETPC()); } void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_b(env, addr, val, true); + do_stby_b(env, addr, val, true, GETPC()); } static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, - bool parallel) + bool parallel, uintptr_t ra) { - uintptr_t ra = GETPC(); - switch (addr & 3) { case 3: /* The 3 byte store must appear atomic. */ @@ -151,13 +147,13 @@ static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_e(env, addr, val, false); + do_stby_e(env, addr, val, false, GETPC()); } void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_e(env, addr, val, true); + do_stby_e(env, addr, val, true, GETPC()); } target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 51bfd9849d..b4fd307b77 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -278,9 +278,63 @@ typedef struct DisasContext { bool psw_n_nonzero; } DisasContext; -/* Target-specific return values from translate_one, indicating the - state of the TB. Note that DISAS_NEXT indicates that we are not - exiting the TB. */ +/* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ +static int expand_sm_imm(int val) +{ + if (val & PSW_SM_E) { + val = (val & ~PSW_SM_E) | PSW_E; + } + if (val & PSW_SM_W) { + val = (val & ~PSW_SM_W) | PSW_W; + } + return val; +} + +/* Inverted space register indicates 0 means sr0 not inferred from base. */ +static int expand_sr3x(int val) +{ + return ~val; +} + +/* Convert the M:A bits within a memory insn to the tri-state value + we use for the final M. */ +static int ma_to_m(int val) +{ + return val & 2 ? (val & 1 ? -1 : 1) : 0; +} + +/* Convert the sign of the displacement to a pre or post-modify. */ +static int pos_to_m(int val) +{ + return val ? 1 : -1; +} + +static int neg_to_m(int val) +{ + return val ? -1 : 1; +} + +/* Used for branch targets and fp memory ops. */ +static int expand_shl2(int val) +{ + return val << 2; +} + +/* Used for fp memory ops. */ +static int expand_shl3(int val) +{ + return val << 3; +} + +/* Used for assemble_21. */ +static int expand_shl11(int val) +{ + return val << 11; +} + + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" /* We are not using a goto_tb (for whatever reason), but have updated the iaq (for whatever reason), so don't do it again on exit. */ @@ -294,21 +348,6 @@ typedef struct DisasContext { to recognize unmasked interrupts. */ #define DISAS_IAQ_N_STALE_EXIT DISAS_TARGET_2 -typedef struct DisasInsn { - uint32_t insn, mask; - DisasJumpType (*trans)(DisasContext *ctx, uint32_t insn, - const struct DisasInsn *f); - union { - void (*ttt)(TCGv_reg, TCGv_reg, TCGv_reg); - void (*weww)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32); - void (*dedd)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64); - void (*wew)(TCGv_i32, TCGv_env, TCGv_i32); - void (*ded)(TCGv_i64, TCGv_env, TCGv_i64); - void (*wed)(TCGv_i32, TCGv_env, TCGv_i64); - void (*dew)(TCGv_i64, TCGv_env, TCGv_i32); - } f; -} DisasInsn; - /* global register indexes */ static TCGv_reg cpu_gr[32]; static TCGv_i64 cpu_sr[4]; @@ -393,6 +432,15 @@ static DisasCond cond_make_f(void) }; } +static DisasCond cond_make_t(void) +{ + return (DisasCond){ + .c = TCG_COND_ALWAYS, + .a0 = NULL, + .a1 = NULL, + }; +} + static DisasCond cond_make_n(void) { return (DisasCond){ @@ -404,15 +452,19 @@ static DisasCond cond_make_n(void) }; } -static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) +static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) { - DisasCond r = { .c = c, .a1 = NULL, .a1_is_0 = true }; - assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - r.a0 = tcg_temp_new(); - tcg_gen_mov_reg(r.a0, a0); + return (DisasCond){ + .c = c, .a0 = a0, .a1_is_0 = true + }; +} - return r; +static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) +{ + TCGv_reg tmp = tcg_temp_new(); + tcg_gen_mov_reg(tmp, a0); + return cond_make_0_tmp(c, tmp); } static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) @@ -665,10 +717,12 @@ static void nullify_set(DisasContext *ctx, bool x) } /* Mark the end of an instruction that may have been nullified. - This is the pair to nullify_over. */ -static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status) + This is the pair to nullify_over. Always returns true so that + it may be tail-called from a translate function. */ +static bool nullify_end(DisasContext *ctx) { TCGLabel *null_lab = ctx->null_lab; + DisasJumpType status = ctx->base.is_jmp; /* For NEXT, NORETURN, STALE, we can easily continue (or exit). For UPDATED, we cannot update on the nullified path. */ @@ -678,7 +732,7 @@ static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status) /* The current insn wasn't conditional or handled the condition applied to it without a branch, so the (new) setting of NULL_COND can be applied directly to the next insn. */ - return status; + return true; } ctx->null_lab = NULL; @@ -696,9 +750,9 @@ static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status) ctx->null_cond = cond_make_n(); } if (status == DISAS_NORETURN) { - status = DISAS_NEXT; + ctx->base.is_jmp = DISAS_NEXT; } - return status; + return true; } static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval) @@ -722,41 +776,49 @@ static void gen_excp_1(int exception) tcg_temp_free_i32(t); } -static DisasJumpType gen_excp(DisasContext *ctx, int exception) +static void gen_excp(DisasContext *ctx, int exception) { copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); gen_excp_1(exception); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } -static DisasJumpType gen_excp_iir(DisasContext *ctx, int exc) +static bool gen_excp_iir(DisasContext *ctx, int exc) { - TCGv_reg tmp = tcg_const_reg(ctx->insn); + TCGv_reg tmp; + + nullify_over(ctx); + tmp = tcg_const_reg(ctx->insn); tcg_gen_st_reg(tmp, cpu_env, offsetof(CPUHPPAState, cr[CR_IIR])); tcg_temp_free(tmp); - return gen_excp(ctx, exc); + gen_excp(ctx, exc); + return nullify_end(ctx); } -static DisasJumpType gen_illegal(DisasContext *ctx) +static bool gen_illegal(DisasContext *ctx) { - nullify_over(ctx); - return nullify_end(ctx, gen_excp_iir(ctx, EXCP_ILL)); + return gen_excp_iir(ctx, EXCP_ILL); } -#define CHECK_MOST_PRIVILEGED(EXCP) \ - do { \ - if (ctx->privilege != 0) { \ - nullify_over(ctx); \ - return nullify_end(ctx, gen_excp_iir(ctx, EXCP)); \ - } \ +#ifdef CONFIG_USER_ONLY +#define CHECK_MOST_PRIVILEGED(EXCP) \ + return gen_excp_iir(ctx, EXCP) +#else +#define CHECK_MOST_PRIVILEGED(EXCP) \ + do { \ + if (ctx->privilege != 0) { \ + return gen_excp_iir(ctx, EXCP); \ + } \ } while (0) +#endif static bool use_goto_tb(DisasContext *ctx, target_ureg dest) { /* Suppress goto_tb in the case of single-steping and IO. */ - if ((tb_cflags(ctx->base.tb) & CF_LAST_IO) || ctx->base.singlestep_enabled) { + if ((tb_cflags(ctx->base.tb) & CF_LAST_IO) + || ctx->base.singlestep_enabled) { return false; } return true; @@ -791,111 +853,20 @@ static void gen_goto_tb(DisasContext *ctx, int which, } } -/* PA has a habit of taking the LSB of a field and using that as the sign, - with the rest of the field becoming the least significant bits. */ -static target_sreg low_sextract(uint32_t val, int pos, int len) -{ - target_ureg x = -(target_ureg)extract32(val, pos, 1); - x = (x << (len - 1)) | extract32(val, pos + 1, len - 1); - return x; -} - -static unsigned assemble_rt64(uint32_t insn) -{ - unsigned r1 = extract32(insn, 6, 1); - unsigned r0 = extract32(insn, 0, 5); - return r1 * 32 + r0; -} - -static unsigned assemble_ra64(uint32_t insn) +static bool cond_need_sv(int c) { - unsigned r1 = extract32(insn, 7, 1); - unsigned r0 = extract32(insn, 21, 5); - return r1 * 32 + r0; + return c == 2 || c == 3 || c == 6; } -static unsigned assemble_rb64(uint32_t insn) +static bool cond_need_cb(int c) { - unsigned r1 = extract32(insn, 12, 1); - unsigned r0 = extract32(insn, 16, 5); - return r1 * 32 + r0; + return c == 4 || c == 5; } -static unsigned assemble_rc64(uint32_t insn) -{ - unsigned r2 = extract32(insn, 8, 1); - unsigned r1 = extract32(insn, 13, 3); - unsigned r0 = extract32(insn, 9, 2); - return r2 * 32 + r1 * 4 + r0; -} - -static unsigned assemble_sr3(uint32_t insn) -{ - unsigned s2 = extract32(insn, 13, 1); - unsigned s0 = extract32(insn, 14, 2); - return s2 * 4 + s0; -} - -static target_sreg assemble_12(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 1) | extract32(insn, 2, 1); - x = (x << 10) | extract32(insn, 3, 10); - return x; -} - -static target_sreg assemble_16(uint32_t insn) -{ - /* Take the name from PA2.0, which produces a 16-bit number - only with wide mode; otherwise a 14-bit number. Since we don't - implement wide mode, this is always the 14-bit number. */ - return low_sextract(insn, 0, 14); -} - -static target_sreg assemble_16a(uint32_t insn) -{ - /* Take the name from PA2.0, which produces a 14-bit shifted number - only with wide mode; otherwise a 12-bit shifted number. Since we - don't implement wide mode, this is always the 12-bit number. */ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 11) | extract32(insn, 2, 11); - return x << 2; -} - -static target_sreg assemble_17(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 5) | extract32(insn, 16, 5); - x = (x << 1) | extract32(insn, 2, 1); - x = (x << 10) | extract32(insn, 3, 10); - return x << 2; -} - -static target_sreg assemble_21(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 11) | extract32(insn, 1, 11); - x = (x << 2) | extract32(insn, 14, 2); - x = (x << 5) | extract32(insn, 16, 5); - x = (x << 2) | extract32(insn, 12, 2); - return x << 11; -} - -static target_sreg assemble_22(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 10) | extract32(insn, 16, 10); - x = (x << 1) | extract32(insn, 2, 1); - x = (x << 10) | extract32(insn, 3, 10); - return x << 2; -} - -/* The parisc documentation describes only the general interpretation of - the conditions, without describing their exact implementation. The - interpretations do not stand up well when considering ADD,C and SUB,B. - However, considering the Addition, Subtraction and Logical conditions - as a whole it would appear that these relations are similar to what - a traditional NZCV set of flags would produce. */ +/* + * Compute conditional for arithmetic. See Page 5-3, Table 5-1, of + * the Parisc 1.1 Architecture Reference Manual for details. + */ static DisasCond do_cond(unsigned cf, TCGv_reg res, TCGv_reg cb_msb, TCGv_reg sv) @@ -904,17 +875,32 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, TCGv_reg tmp; switch (cf >> 1) { - case 0: /* Never / TR */ + case 0: /* Never / TR (0 / 1) */ cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ cond = cond_make_0(TCG_COND_EQ, res); break; - case 2: /* < / >= (N / !N) */ - cond = cond_make_0(TCG_COND_LT, res); + case 2: /* < / >= (N ^ V / !(N ^ V) */ + tmp = tcg_temp_new(); + tcg_gen_xor_reg(tmp, res, sv); + cond = cond_make_0_tmp(TCG_COND_LT, tmp); break; - case 3: /* <= / > (N | Z / !N & !Z) */ - cond = cond_make_0(TCG_COND_LE, res); + case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ + /* + * Simplify: + * (N ^ V) | Z + * ((res < 0) ^ (sv < 0)) | !res + * ((res ^ sv) < 0) | !res + * (~(res ^ sv) >= 0) | !res + * !(~(res ^ sv) >> 31) | !res + * !(~(res ^ sv) >> 31 & res) + */ + tmp = tcg_temp_new(); + tcg_gen_eqv_reg(tmp, res, sv); + tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); + tcg_gen_and_reg(tmp, tmp, res); + cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 4: /* NUV / UV (!C / C) */ cond = cond_make_0(TCG_COND_EQ, cb_msb); @@ -923,8 +909,7 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, tmp = tcg_temp_new(); tcg_gen_neg_reg(tmp, cb_msb); tcg_gen_and_reg(tmp, tmp, res); - cond = cond_make_0(TCG_COND_EQ, tmp); - tcg_temp_free(tmp); + cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 6: /* SV / NSV (V / !V) */ cond = cond_make_0(TCG_COND_LT, sv); @@ -932,8 +917,7 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, case 7: /* OD / EV */ tmp = tcg_temp_new(); tcg_gen_andi_reg(tmp, res, 1); - cond = cond_make_0(TCG_COND_NE, tmp); - tcg_temp_free(tmp); + cond = cond_make_0_tmp(TCG_COND_NE, tmp); break; default: g_assert_not_reached(); @@ -971,7 +955,7 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, cond = cond_make(TCG_COND_LEU, in1, in2); break; default: - return do_cond(cf, res, sv, sv); + return do_cond(cf, res, NULL, sv); } if (cf & 1) { cond.c = tcg_invert_cond(cond.c); @@ -980,17 +964,50 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, return cond; } -/* Similar, but for logicals, where the carry and overflow bits are not - computed, and use of them is undefined. */ +/* + * Similar, but for logicals, where the carry and overflow bits are not + * computed, and use of them is undefined. + * + * Undefined or not, hardware does not trap. It seems reasonable to + * assume hardware treats cases c={4,5,6} as if C=0 & V=0, since that's + * how cases c={2,3} are treated. + */ static DisasCond do_log_cond(unsigned cf, TCGv_reg res) { - switch (cf >> 1) { - case 4: case 5: case 6: - cf &= 1; - break; + switch (cf) { + case 0: /* never */ + case 9: /* undef, C */ + case 11: /* undef, C & !Z */ + case 12: /* undef, V */ + return cond_make_f(); + + case 1: /* true */ + case 8: /* undef, !C */ + case 10: /* undef, !C | Z */ + case 13: /* undef, !V */ + return cond_make_t(); + + case 2: /* == */ + return cond_make_0(TCG_COND_EQ, res); + case 3: /* <> */ + return cond_make_0(TCG_COND_NE, res); + case 4: /* < */ + return cond_make_0(TCG_COND_LT, res); + case 5: /* >= */ + return cond_make_0(TCG_COND_GE, res); + case 6: /* <= */ + return cond_make_0(TCG_COND_LE, res); + case 7: /* > */ + return cond_make_0(TCG_COND_GT, res); + + case 14: /* OD */ + case 15: /* EV */ + return do_cond(cf, res, NULL, NULL); + + default: + g_assert_not_reached(); } - return do_cond(cf, res, res, res); } /* Similar, but for shift/extract/deposit conditions. */ @@ -1119,9 +1136,9 @@ static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res, return sv; } -static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned shift, bool is_l, - bool is_tsv, bool is_tc, bool is_c, unsigned cf) +static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned shift, bool is_l, + bool is_tsv, bool is_tc, bool is_c, unsigned cf) { TCGv_reg dest, cb, cb_msb, sv, tmp; unsigned c = cf >> 1; @@ -1137,7 +1154,7 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, in1 = tmp; } - if (!is_l || c == 4 || c == 5) { + if (!is_l || cond_need_cb(c)) { TCGv_reg zero = tcg_const_reg(0); cb_msb = get_temp(ctx); tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); @@ -1159,7 +1176,7 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute signed overflow if required. */ sv = NULL; - if (is_tsv || c == 6) { + if (is_tsv || cond_need_sv(c)) { sv = do_add_sv(ctx, dest, in1, in2); if (is_tsv) { /* ??? Need to include overflow from shift. */ @@ -1188,12 +1205,39 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; - return DISAS_NEXT; } -static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, bool is_tsv, bool is_b, - bool is_tc, unsigned cf) +static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a, + bool is_l, bool is_tsv, bool is_tc, bool is_c) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, is_tsv, is_tc, is_c, a->cf); + return nullify_end(ctx); +} + +static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, + bool is_tsv, bool is_tc) +{ + TCGv_reg tcg_im, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_im = load_const(ctx, a->i); + tcg_r2 = load_gpr(ctx, a->r); + do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf); + return nullify_end(ctx); +} + +static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, bool is_tsv, bool is_b, + bool is_tc, unsigned cf) { TCGv_reg dest, sv, cb, cb_msb, zero, tmp; unsigned c = cf >> 1; @@ -1223,7 +1267,7 @@ static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute signed overflow if required. */ sv = NULL; - if (is_tsv || c == 6) { + if (is_tsv || cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); if (is_tsv) { gen_helper_tsv(cpu_env, sv); @@ -1255,11 +1299,37 @@ static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; - return DISAS_NEXT; } -static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf) +static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a, + bool is_tsv, bool is_b, bool is_tc) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf); + return nullify_end(ctx); +} + +static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) +{ + TCGv_reg tcg_im, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_im = load_const(ctx, a->i); + tcg_r2 = load_gpr(ctx, a->r); + do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf); + return nullify_end(ctx); +} + +static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned cf) { TCGv_reg dest, sv; DisasCond cond; @@ -1269,7 +1339,7 @@ static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute signed overflow if required. */ sv = NULL; - if ((cf >> 1) == 6) { + if (cond_need_sv(cf >> 1)) { sv = do_sub_sv(ctx, dest, in1, in2); } @@ -1284,12 +1354,11 @@ static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; - return DISAS_NEXT; } -static DisasJumpType do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned cf, + void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest = dest_gpr(ctx, rt); @@ -1302,12 +1371,25 @@ static DisasJumpType do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (cf) { ctx->null_cond = do_log_cond(cf, dest); } - return DISAS_NEXT; } -static DisasJumpType do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool is_tc, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static bool do_log_reg(DisasContext *ctx, arg_rrr_cf *a, + void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, fn); + return nullify_end(ctx); +} + +static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned cf, bool is_tc, + void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest; DisasCond cond; @@ -1335,7 +1417,6 @@ static DisasJumpType do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, cond_free(&ctx->null_cond); ctx->null_cond = cond; } - return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY @@ -1498,9 +1579,9 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, #define do_store_reg do_store_32 #endif -static DisasJumpType do_load(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify, TCGMemOp mop) +static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify, TCGMemOp mop) { TCGv_reg dest; @@ -1516,12 +1597,12 @@ static DisasJumpType do_load(DisasContext *ctx, unsigned rt, unsigned rb, do_load_reg(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i32 tmp; @@ -1536,12 +1617,18 @@ static DisasJumpType do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, gen_helper_loaded_fr0(cpu_env); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); +} + +static bool trans_fldw(DisasContext *ctx, arg_ldst *a) +{ + return do_floadw(ctx, a->t, a->b, a->x, a->scale ? 2 : 0, + a->disp, a->sp, a->m); } -static DisasJumpType do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i64 tmp; @@ -1556,21 +1643,27 @@ static DisasJumpType do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, gen_helper_loaded_fr0(cpu_env); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); +} + +static bool trans_fldd(DisasContext *ctx, arg_ldst *a) +{ + return do_floadd(ctx, a->t, a->b, a->x, a->scale ? 3 : 0, + a->disp, a->sp, a->m); } -static DisasJumpType do_store(DisasContext *ctx, unsigned rt, unsigned rb, - target_sreg disp, unsigned sp, - int modify, TCGMemOp mop) +static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, + target_sreg disp, unsigned sp, + int modify, TCGMemOp mop) { nullify_over(ctx); do_store_reg(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i32 tmp; @@ -1580,12 +1673,18 @@ static DisasJumpType do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL); tcg_temp_free_i32(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); +} + +static bool trans_fstw(DisasContext *ctx, arg_ldst *a) +{ + return do_fstorew(ctx, a->t, a->b, a->x, a->scale ? 2 : 0, + a->disp, a->sp, a->m); } -static DisasJumpType do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i64 tmp; @@ -1595,11 +1694,17 @@ static DisasJumpType do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEQ); tcg_temp_free_i64(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) +static bool trans_fstd(DisasContext *ctx, arg_ldst *a) +{ + return do_fstored(ctx, a->t, a->b, a->x, a->scale ? 3 : 0, + a->disp, a->sp, a->m); +} + +static bool do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) { TCGv_i32 tmp; @@ -1610,11 +1715,11 @@ static DisasJumpType do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, save_frw_i32(rt, tmp); tcg_temp_free_i32(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) +static bool do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) { TCGv_i32 dst; TCGv_i64 src; @@ -1628,11 +1733,11 @@ static DisasJumpType do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, tcg_temp_free_i64(src); save_frw_i32(rt, dst); tcg_temp_free_i32(dst); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) +static bool do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) { TCGv_i64 tmp; @@ -1643,11 +1748,11 @@ static DisasJumpType do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, save_frd(rt, tmp); tcg_temp_free_i64(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) +static bool do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) { TCGv_i32 src; TCGv_i64 dst; @@ -1661,13 +1766,12 @@ static DisasJumpType do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, tcg_temp_free_i32(src); save_frd(rt, dst); tcg_temp_free_i64(dst); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_weww(DisasContext *ctx, unsigned rt, - unsigned ra, unsigned rb, - void (*func)(TCGv_i32, TCGv_env, - TCGv_i32, TCGv_i32)) +static bool do_fop_weww(DisasContext *ctx, unsigned rt, + unsigned ra, unsigned rb, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) { TCGv_i32 a, b; @@ -1680,13 +1784,12 @@ static DisasJumpType do_fop_weww(DisasContext *ctx, unsigned rt, tcg_temp_free_i32(b); save_frw_i32(rt, a); tcg_temp_free_i32(a); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_dedd(DisasContext *ctx, unsigned rt, - unsigned ra, unsigned rb, - void (*func)(TCGv_i64, TCGv_env, - TCGv_i64, TCGv_i64)) +static bool do_fop_dedd(DisasContext *ctx, unsigned rt, + unsigned ra, unsigned rb, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) { TCGv_i64 a, b; @@ -1699,13 +1802,13 @@ static DisasJumpType do_fop_dedd(DisasContext *ctx, unsigned rt, tcg_temp_free_i64(b); save_frd(rt, a); tcg_temp_free_i64(a); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } /* Emit an unconditional branch to a direct target, which may or may not have already had nullification handled. */ -static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest, - unsigned link, bool is_n) +static bool do_dbranch(DisasContext *ctx, target_ureg dest, + unsigned link, bool is_n) { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (link != 0) { @@ -1715,7 +1818,6 @@ static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest, if (is_n) { ctx->null_cond.c = TCG_COND_ALWAYS; } - return DISAS_NEXT; } else { nullify_over(ctx); @@ -1731,18 +1833,19 @@ static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest, gen_goto_tb(ctx, 0, ctx->iaoq_b, dest); } - nullify_end(ctx, DISAS_NEXT); + nullify_end(ctx); nullify_set(ctx, 0); gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } + return true; } /* Emit a conditional branch to a direct target. If the branch itself is nullified, we should have already used nullify_over. */ -static DisasJumpType do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, - DisasCond *cond) +static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, + DisasCond *cond) { target_ureg dest = iaoq_dest(ctx, disp); TCGLabel *taken = NULL; @@ -1799,16 +1902,17 @@ static DisasJumpType do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, if (ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; - return DISAS_IAQ_N_STALE; + ctx->base.is_jmp = DISAS_IAQ_N_STALE; } else { - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } + return true; } /* Emit an unconditional branch to an indirect target. This handles nullification of the branch itself. */ -static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, - unsigned link, bool is_n) +static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, + unsigned link, bool is_n) { TCGv_reg a0, a1, next, tmp; TCGCond c; @@ -1826,7 +1930,8 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, tcg_gen_mov_reg(cpu_iaoq_f, next); tcg_gen_addi_reg(cpu_iaoq_b, next, 4); nullify_set(ctx, 0); - return DISAS_IAQ_N_UPDATED; + ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } @@ -1853,7 +1958,7 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n); } tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } else { cond_prep(&ctx->null_cond); c = ctx->null_cond.c; @@ -1884,8 +1989,7 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, cond_free(&ctx->null_cond); } } - - return DISAS_NEXT; + return true; } /* Implement @@ -1926,7 +2030,7 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) in than the "be disp(sr2,r0)" instruction that probably sent us here, is the easiest way to handle the branch delay slot on the aforementioned BE. */ -static DisasJumpType do_page_zero(DisasContext *ctx) +static void do_page_zero(DisasContext *ctx) { /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the @@ -1954,71 +2058,70 @@ static DisasJumpType do_page_zero(DisasContext *ctx) switch (ctx->iaoq_f & -4) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; case 0xb0: /* LWS */ gen_excp_1(EXCP_SYSCALL_LWS); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; case 0xe0: /* SET_THREAD_POINTER */ tcg_gen_st_reg(cpu_gr[26], cpu_env, offsetof(CPUHPPAState, cr[27])); tcg_gen_ori_reg(cpu_iaoq_f, cpu_gr[31], 3); tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); - return DISAS_IAQ_N_UPDATED; + ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + break; case 0x100: /* SYSCALL */ gen_excp_1(EXCP_SYSCALL); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; default: do_sigill: gen_excp_1(EXCP_ILL); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; } } #endif -static DisasJumpType trans_nop(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_nop(DisasContext *ctx, arg_nop *a) { cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_break(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_break(DisasContext *ctx, arg_break *a) { - nullify_over(ctx); - return nullify_end(ctx, gen_excp_iir(ctx, EXCP_BREAK)); + return gen_excp_iir(ctx, EXCP_BREAK); } -static DisasJumpType trans_sync(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_sync(DisasContext *ctx, arg_sync *a) { /* No point in nullifying the memory barrier. */ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mfia(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mfia(DisasContext *ctx, arg_mfia *a) { - unsigned rt = extract32(insn, 0, 5); + unsigned rt = a->t; TCGv_reg tmp = dest_gpr(ctx, rt); tcg_gen_movi_reg(tmp, ctx->iaoq_f); save_gpr(ctx, rt, tmp); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mfsp(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned rs = assemble_sr3(insn); + unsigned rt = a->t; + unsigned rs = a->sp; TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_reg t1 = tcg_temp_new(); @@ -2031,21 +2134,19 @@ static DisasJumpType trans_mfsp(DisasContext *ctx, uint32_t insn, tcg_temp_free_i64(t0); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned ctl = extract32(insn, 21, 5); + unsigned rt = a->t; + unsigned ctl = a->r; TCGv_reg tmp; - DisasJumpType ret; switch (ctl) { case CR_SAR: #ifdef TARGET_HPPA64 - if (extract32(insn, 14, 1) == 0) { + if (a->e == 0) { /* MFSAR without ,W masks low 5 bits. */ tmp = dest_gpr(ctx, rt); tcg_gen_andi_reg(tmp, cpu_sar, 31); @@ -2063,13 +2164,12 @@ static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn, gen_io_start(); gen_helper_read_interval_timer(tmp); gen_io_end(); - ret = DISAS_IAQ_N_STALE; + ctx->base.is_jmp = DISAS_IAQ_N_STALE; } else { gen_helper_read_interval_timer(tmp); - ret = DISAS_NEXT; } save_gpr(ctx, rt, tmp); - return nullify_end(ctx, ret); + return nullify_end(ctx); case 26: case 27: break; @@ -2085,14 +2185,13 @@ static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn, done: cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mtsp(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a) { - unsigned rr = extract32(insn, 16, 5); - unsigned rs = assemble_sr3(insn); + unsigned rr = a->r; + unsigned rs = a->sp; TCGv_i64 t64; if (rs >= 5) { @@ -2112,15 +2211,13 @@ static DisasJumpType trans_mtsp(DisasContext *ctx, uint32_t insn, } tcg_temp_free_i64(t64); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) { - unsigned rin = extract32(insn, 16, 5); - unsigned ctl = extract32(insn, 21, 5); - TCGv_reg reg = load_gpr(ctx, rin); + unsigned ctl = a->t; + TCGv_reg reg = load_gpr(ctx, a->r); TCGv_reg tmp; if (ctl == CR_SAR) { @@ -2130,17 +2227,13 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, tcg_temp_free(tmp); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } /* All other control registers are privileged or read-only. */ CHECK_MOST_PRIVILEGED(EXCP_PRIV_REG); -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - DisasJumpType ret = DISAS_NEXT; - +#ifndef CONFIG_USER_ONLY nullify_over(ctx); switch (ctl) { case CR_IT: @@ -2151,7 +2244,7 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, break; case CR_EIEM: gen_helper_write_eiem(cpu_env, reg); - ret = DISAS_IAQ_N_STALE_EXIT; + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; break; case CR_IIASQ: @@ -2170,255 +2263,213 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl])); break; } - return nullify_end(ctx, ret); + return nullify_end(ctx); #endif } -static DisasJumpType trans_mtsarcm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) { - unsigned rin = extract32(insn, 16, 5); TCGv_reg tmp = tcg_temp_new(); - tcg_gen_not_reg(tmp, load_gpr(ctx, rin)); + tcg_gen_not_reg(tmp, load_gpr(ctx, a->r)); tcg_gen_andi_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); save_or_nullify(ctx, cpu_sar, tmp); tcg_temp_free(tmp); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_ldsid(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) { - unsigned rt = extract32(insn, 0, 5); - TCGv_reg dest = dest_gpr(ctx, rt); + TCGv_reg dest = dest_gpr(ctx, a->t); #ifdef CONFIG_USER_ONLY /* We don't implement space registers in user mode. */ tcg_gen_movi_reg(dest, 0); #else - unsigned rb = extract32(insn, 21, 5); - unsigned sp = extract32(insn, 14, 2); TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_mov_i64(t0, space_select(ctx, sp, load_gpr(ctx, rb))); + tcg_gen_mov_i64(t0, space_select(ctx, a->sp, load_gpr(ctx, a->b))); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_trunc_i64_reg(dest, t0); tcg_temp_free_i64(t0); #endif - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); cond_free(&ctx->null_cond); - return DISAS_NEXT; -} - -#ifndef CONFIG_USER_ONLY -/* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ -static target_ureg extract_sm_imm(uint32_t insn) -{ - target_ureg val = extract32(insn, 16, 10); - - if (val & PSW_SM_E) { - val = (val & ~PSW_SM_E) | PSW_E; - } - if (val & PSW_SM_W) { - val = (val & ~PSW_SM_W) | PSW_W; - } - return val; + return true; } -static DisasJumpType trans_rsm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_rsm(DisasContext *ctx, arg_rsm *a) { - unsigned rt = extract32(insn, 0, 5); - target_ureg sm = extract_sm_imm(insn); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_reg tmp; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); tmp = get_temp(ctx); tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw)); - tcg_gen_andi_reg(tmp, tmp, ~sm); + tcg_gen_andi_reg(tmp, tmp, ~a->i); gen_helper_swap_system_mask(tmp, cpu_env, tmp); - save_gpr(ctx, rt, tmp); + save_gpr(ctx, a->t, tmp); /* Exit the TB to recognize new interrupts, e.g. PSW_M. */ - return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT); + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + return nullify_end(ctx); +#endif } -static DisasJumpType trans_ssm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ssm(DisasContext *ctx, arg_ssm *a) { - unsigned rt = extract32(insn, 0, 5); - target_ureg sm = extract_sm_imm(insn); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_reg tmp; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); tmp = get_temp(ctx); tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw)); - tcg_gen_ori_reg(tmp, tmp, sm); + tcg_gen_ori_reg(tmp, tmp, a->i); gen_helper_swap_system_mask(tmp, cpu_env, tmp); - save_gpr(ctx, rt, tmp); + save_gpr(ctx, a->t, tmp); /* Exit the TB to recognize new interrupts, e.g. PSW_I. */ - return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT); + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + return nullify_end(ctx); +#endif } -static DisasJumpType trans_mtsm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a) { - unsigned rr = extract32(insn, 16, 5); - TCGv_reg tmp, reg; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + TCGv_reg tmp, reg; nullify_over(ctx); - reg = load_gpr(ctx, rr); + reg = load_gpr(ctx, a->r); tmp = get_temp(ctx); gen_helper_swap_system_mask(tmp, cpu_env, reg); /* Exit the TB to recognize new interrupts. */ - return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT); + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + return nullify_end(ctx); +#endif } -static DisasJumpType trans_rfi(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_rfi(DisasContext *ctx, bool rfi_r) { - unsigned comp = extract32(insn, 5, 4); - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY nullify_over(ctx); - if (comp == 5) { + if (rfi_r) { gen_helper_rfi_r(cpu_env); } else { gen_helper_rfi(cpu_env); } + /* Exit the TB to recognize new interrupts. */ if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); } else { tcg_gen_exit_tb(NULL, 0); } + ctx->base.is_jmp = DISAS_NORETURN; - /* Exit the TB to recognize new interrupts. */ - return nullify_end(ctx, DISAS_NORETURN); + return nullify_end(ctx); +#endif +} + +static bool trans_rfi(DisasContext *ctx, arg_rfi *a) +{ + return do_rfi(ctx, false); } -static DisasJumpType gen_hlt(DisasContext *ctx, int reset) +static bool trans_rfi_r(DisasContext *ctx, arg_rfi_r *a) +{ + return do_rfi(ctx, true); +} + +static bool trans_halt(DisasContext *ctx, arg_halt *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - nullify_over(ctx); - if (reset) { - gen_helper_reset(cpu_env); - } else { - gen_helper_halt(cpu_env); - } - return nullify_end(ctx, DISAS_NORETURN); -} -#endif /* !CONFIG_USER_ONLY */ - -static const DisasInsn table_system[] = { - { 0x00000000u, 0xfc001fe0u, trans_break }, - { 0x00001820u, 0xffe01fffu, trans_mtsp }, - { 0x00001840u, 0xfc00ffffu, trans_mtctl }, - { 0x016018c0u, 0xffe0ffffu, trans_mtsarcm }, - { 0x000014a0u, 0xffffffe0u, trans_mfia }, - { 0x000004a0u, 0xffff1fe0u, trans_mfsp }, - { 0x000008a0u, 0xfc1fbfe0u, trans_mfctl }, - { 0x00000400u, 0xffffffffu, trans_sync }, /* sync */ - { 0x00100400u, 0xffffffffu, trans_sync }, /* syncdma */ - { 0x000010a0u, 0xfc1f3fe0u, trans_ldsid }, #ifndef CONFIG_USER_ONLY - { 0x00000e60u, 0xfc00ffe0u, trans_rsm }, - { 0x00000d60u, 0xfc00ffe0u, trans_ssm }, - { 0x00001860u, 0xffe0ffffu, trans_mtsm }, - { 0x00000c00u, 0xfffffe1fu, trans_rfi }, + nullify_over(ctx); + gen_helper_halt(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); #endif -}; +} -static DisasJumpType trans_base_idx_mod(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_reset(DisasContext *ctx, arg_reset *a) { - unsigned rb = extract32(insn, 21, 5); - unsigned rx = extract32(insn, 16, 5); - TCGv_reg dest = dest_gpr(ctx, rb); - TCGv_reg src1 = load_gpr(ctx, rb); - TCGv_reg src2 = load_gpr(ctx, rx); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + gen_helper_reset(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); +#endif +} - /* The only thing we need to do is the base register modification. */ - tcg_gen_add_reg(dest, src1, src2); - save_gpr(ctx, rb, dest); +static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) +{ + if (a->m) { + TCGv_reg dest = dest_gpr(ctx, a->b); + TCGv_reg src1 = load_gpr(ctx, a->b); + TCGv_reg src2 = load_gpr(ctx, a->x); + /* The only thing we need to do is the base register modification. */ + tcg_gen_add_reg(dest, src1, src2); + save_gpr(ctx, a->b, dest); + } cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_probe(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_probe(DisasContext *ctx, arg_probe *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned sp = extract32(insn, 14, 2); - unsigned rr = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned is_write = extract32(insn, 6, 1); - unsigned is_imm = extract32(insn, 13, 1); TCGv_reg dest, ofs; TCGv_i32 level, want; TCGv_tl addr; nullify_over(ctx); - dest = dest_gpr(ctx, rt); - form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false); + dest = dest_gpr(ctx, a->t); + form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); - if (is_imm) { - level = tcg_const_i32(extract32(insn, 16, 2)); + if (a->imm) { + level = tcg_const_i32(a->ri); } else { level = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(level, load_gpr(ctx, rr)); + tcg_gen_trunc_reg_i32(level, load_gpr(ctx, a->ri)); tcg_gen_andi_i32(level, level, 3); } - want = tcg_const_i32(is_write ? PAGE_WRITE : PAGE_READ); + want = tcg_const_i32(a->write ? PAGE_WRITE : PAGE_READ); gen_helper_probe(dest, cpu_env, addr, level, want); tcg_temp_free_i32(want); tcg_temp_free_i32(level); - save_gpr(ctx, rt, dest); - return nullify_end(ctx, DISAS_NEXT); + save_gpr(ctx, a->t, dest); + return nullify_end(ctx); } +static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY -static DisasJumpType trans_ixtlbx(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned sp; - unsigned rr = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned is_data = insn & 0x1000; - unsigned is_addr = insn & 0x40; TCGv_tl addr; TCGv_reg ofs, reg; - if (is_data) { - sp = extract32(insn, 14, 2); - } else { - sp = ~assemble_sr3(insn); - } - - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); - form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false); - reg = load_gpr(ctx, rr); - if (is_addr) { + form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); + reg = load_gpr(ctx, a->r); + if (a->addr) { gen_helper_itlba(cpu_env, addr, reg); } else { gen_helper_itlbp(cpu_env, addr, reg); @@ -2426,79 +2477,67 @@ static DisasJumpType trans_ixtlbx(DisasContext *ctx, uint32_t insn, /* Exit TB for ITLB change if mmu is enabled. This *should* not be the case, since the OS TLB fill handler runs with mmu disabled. */ - return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C) - ? DISAS_IAQ_N_STALE : DISAS_NEXT); + if (!a->data && (ctx->tb_flags & PSW_C)) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif } -static DisasJumpType trans_pxtlbx(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) { - unsigned m = extract32(insn, 5, 1); - unsigned sp; - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned is_data = insn & 0x1000; - unsigned is_local = insn & 0x40; + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_tl addr; TCGv_reg ofs; - if (is_data) { - sp = extract32(insn, 14, 2); - } else { - sp = ~assemble_sr3(insn); - } - - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); - form_gva(ctx, &addr, &ofs, rb, rx, 0, 0, sp, m, false); - if (m) { - save_gpr(ctx, rb, ofs); + form_gva(ctx, &addr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); + if (a->m) { + save_gpr(ctx, a->b, ofs); } - if (is_local) { + if (a->local) { gen_helper_ptlbe(cpu_env); } else { gen_helper_ptlb(cpu_env, addr); } /* Exit TB for TLB change if mmu is enabled. */ - return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C) - ? DISAS_IAQ_N_STALE : DISAS_NEXT); + if (!a->data && (ctx->tb_flags & PSW_C)) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif } -static DisasJumpType trans_lpa(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_lpa(DisasContext *ctx, arg_ldst *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_tl vaddr; TCGv_reg ofs, paddr; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); - form_gva(ctx, &vaddr, &ofs, rb, rx, 0, 0, sp, m, false); + form_gva(ctx, &vaddr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); paddr = tcg_temp_new(); gen_helper_lpa(paddr, cpu_env, vaddr); /* Note that physical address result overrides base modification. */ - if (m) { - save_gpr(ctx, rb, ofs); + if (a->m) { + save_gpr(ctx, a->b, ofs); } - save_gpr(ctx, rt, paddr); + save_gpr(ctx, a->t, paddr); tcg_temp_free(paddr); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); +#endif } -static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_lci(DisasContext *ctx, arg_lci *a) { - unsigned rt = extract32(insn, 0, 5); TCGv_reg ci; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); @@ -2508,238 +2547,193 @@ static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn, view of the cache. Our implementation is to return 0 for all, since the entire address space is coherent. */ ci = tcg_const_reg(0); - save_gpr(ctx, rt, ci); + save_gpr(ctx, a->t, ci); tcg_temp_free(ci); - return DISAS_NEXT; -} -#endif /* !CONFIG_USER_ONLY */ - -static const DisasInsn table_mem_mgmt[] = { - { 0x04003280u, 0xfc003fffu, trans_nop }, /* fdc, disp */ - { 0x04001280u, 0xfc003fffu, trans_nop }, /* fdc, index */ - { 0x040012a0u, 0xfc003fffu, trans_base_idx_mod }, /* fdc, index, base mod */ - { 0x040012c0u, 0xfc003fffu, trans_nop }, /* fdce */ - { 0x040012e0u, 0xfc003fffu, trans_base_idx_mod }, /* fdce, base mod */ - { 0x04000280u, 0xfc001fffu, trans_nop }, /* fic 0a */ - { 0x040002a0u, 0xfc001fffu, trans_base_idx_mod }, /* fic 0a, base mod */ - { 0x040013c0u, 0xfc003fffu, trans_nop }, /* fic 4f */ - { 0x040013e0u, 0xfc003fffu, trans_base_idx_mod }, /* fic 4f, base mod */ - { 0x040002c0u, 0xfc001fffu, trans_nop }, /* fice */ - { 0x040002e0u, 0xfc001fffu, trans_base_idx_mod }, /* fice, base mod */ - { 0x04002700u, 0xfc003fffu, trans_nop }, /* pdc */ - { 0x04002720u, 0xfc003fffu, trans_base_idx_mod }, /* pdc, base mod */ - { 0x04001180u, 0xfc003fa0u, trans_probe }, /* probe */ - { 0x04003180u, 0xfc003fa0u, trans_probe }, /* probei */ -#ifndef CONFIG_USER_ONLY - { 0x04000000u, 0xfc001fffu, trans_ixtlbx }, /* iitlbp */ - { 0x04000040u, 0xfc001fffu, trans_ixtlbx }, /* iitlba */ - { 0x04001000u, 0xfc001fffu, trans_ixtlbx }, /* idtlbp */ - { 0x04001040u, 0xfc001fffu, trans_ixtlbx }, /* idtlba */ - { 0x04000200u, 0xfc001fdfu, trans_pxtlbx }, /* pitlb */ - { 0x04000240u, 0xfc001fdfu, trans_pxtlbx }, /* pitlbe */ - { 0x04001200u, 0xfc001fdfu, trans_pxtlbx }, /* pdtlb */ - { 0x04001240u, 0xfc001fdfu, trans_pxtlbx }, /* pdtlbe */ - { 0x04001340u, 0xfc003fc0u, trans_lpa }, - { 0x04001300u, 0xfc003fe0u, trans_lci }, -#endif -}; + cond_free(&ctx->null_cond); + return true; +} -static DisasJumpType trans_add(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_add(DisasContext *ctx, arg_rrr_cf_sh *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned ext = extract32(insn, 8, 4); - unsigned shift = extract32(insn, 6, 2); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - bool is_c = false; - bool is_l = false; - bool is_tc = false; - bool is_tsv = false; - DisasJumpType ret; + return do_add_reg(ctx, a, false, false, false, false); +} - switch (ext) { - case 0x6: /* ADD, SHLADD */ - break; - case 0xa: /* ADD,L, SHLADD,L */ - is_l = true; - break; - case 0xe: /* ADD,TSV, SHLADD,TSV (1) */ - is_tsv = true; - break; - case 0x7: /* ADD,C */ - is_c = true; - break; - case 0xf: /* ADD,C,TSV */ - is_c = is_tsv = true; - break; - default: - return gen_illegal(ctx); - } +static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, true, false, false, false); +} - if (cf) { - nullify_over(ctx); - } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_add(ctx, rt, tcg_r1, tcg_r2, shift, is_l, is_tsv, is_tc, is_c, cf); - return nullify_end(ctx, ret); +static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, false, true, false, false); } -static DisasJumpType trans_sub(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_sh *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned ext = extract32(insn, 6, 6); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - bool is_b = false; - bool is_tc = false; - bool is_tsv = false; - DisasJumpType ret; + return do_add_reg(ctx, a, false, false, false, true); +} - switch (ext) { - case 0x10: /* SUB */ - break; - case 0x30: /* SUB,TSV */ - is_tsv = true; - break; - case 0x14: /* SUB,B */ - is_b = true; - break; - case 0x34: /* SUB,B,TSV */ - is_b = is_tsv = true; - break; - case 0x13: /* SUB,TC */ - is_tc = true; - break; - case 0x33: /* SUB,TSV,TC */ - is_tc = is_tsv = true; - break; - default: - return gen_illegal(ctx); - } +static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, false, true, false, true); +} - if (cf) { - nullify_over(ctx); - } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_sub(ctx, rt, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, cf); - return nullify_end(ctx, ret); +static bool trans_sub(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, false, false, false); } -static DisasJumpType trans_log(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - DisasJumpType ret; + return do_sub_reg(ctx, a, true, false, false); +} - if (cf) { - nullify_over(ctx); - } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_log(ctx, rt, tcg_r1, tcg_r2, cf, di->f.ttt); - return nullify_end(ctx, ret); +static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, false, false, true); } -/* OR r,0,t -> COPY (according to gas) */ -static DisasJumpType trans_copy(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r1 = extract32(insn, 16, 5); - unsigned rt = extract32(insn, 0, 5); + return do_sub_reg(ctx, a, true, false, true); +} - if (r1 == 0) { - TCGv_reg dest = dest_gpr(ctx, rt); - tcg_gen_movi_reg(dest, 0); - save_gpr(ctx, rt, dest); - } else { - save_gpr(ctx, rt, cpu_gr[r1]); +static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, false, true, false); +} + +static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, true, true, false); +} + +static bool trans_andcm(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_log_reg(ctx, a, tcg_gen_andc_reg); +} + +static bool trans_and(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_log_reg(ctx, a, tcg_gen_and_reg); +} + +static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) +{ + if (a->cf == 0) { + unsigned r2 = a->r2; + unsigned r1 = a->r1; + unsigned rt = a->t; + + if (rt == 0) { /* NOP */ + cond_free(&ctx->null_cond); + return true; + } + if (r2 == 0) { /* COPY */ + if (r1 == 0) { + TCGv_reg dest = dest_gpr(ctx, rt); + tcg_gen_movi_reg(dest, 0); + save_gpr(ctx, rt, dest); + } else { + save_gpr(ctx, rt, cpu_gr[r1]); + } + cond_free(&ctx->null_cond); + return true; + } +#ifndef CONFIG_USER_ONLY + /* These are QEMU extensions and are nops in the real architecture: + * + * or %r10,%r10,%r10 -- idle loop; wait for interrupt + * or %r31,%r31,%r31 -- death loop; offline cpu + * currently implemented as idle. + */ + if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */ + TCGv_i32 tmp; + + /* No need to check for supervisor, as userland can only pause + until the next timer interrupt. */ + nullify_over(ctx); + + /* Advance the instruction queue. */ + copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + nullify_set(ctx, 0); + + /* Tell the qemu main loop to halt until this cpu has work. */ + tmp = tcg_const_i32(1); + tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) + + offsetof(CPUState, halted)); + tcg_temp_free_i32(tmp); + gen_excp_1(EXCP_HALTED); + ctx->base.is_jmp = DISAS_NORETURN; + + return nullify_end(ctx); + } +#endif } - cond_free(&ctx->null_cond); - return DISAS_NEXT; + return do_log_reg(ctx, a, tcg_gen_or_reg); +} + +static bool trans_xor(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_log_reg(ctx, a, tcg_gen_xor_reg); } -static DisasJumpType trans_cmpclr(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tcg_r1, tcg_r2; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_cmpclr(ctx, rt, tcg_r1, tcg_r2, cf); - return nullify_end(ctx, ret); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf); + return nullify_end(ctx); } -static DisasJumpType trans_uxor(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_uxor(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tcg_r1, tcg_r2; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_unit(ctx, rt, tcg_r1, tcg_r2, cf, false, tcg_gen_xor_reg); - return nullify_end(ctx, ret); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, false, tcg_gen_xor_reg); + return nullify_end(ctx); } -static DisasJumpType trans_uaddcm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned is_tc = extract32(insn, 6, 1); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tcg_r1, tcg_r2, tmp; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); tmp = get_temp(ctx); tcg_gen_not_reg(tmp, tcg_r2); - ret = do_unit(ctx, rt, tcg_r1, tmp, cf, is_tc, tcg_gen_add_reg); - return nullify_end(ctx, ret); + do_unit(ctx, a->t, tcg_r1, tmp, a->cf, is_tc, tcg_gen_add_reg); + return nullify_end(ctx); +} + +static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_uaddcm(ctx, a, false); +} + +static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_uaddcm(ctx, a, true); } -static DisasJumpType trans_dcor(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i) { - unsigned r2 = extract32(insn, 21, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned is_i = extract32(insn, 6, 1); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tmp; - DisasJumpType ret; nullify_over(ctx); @@ -2750,25 +2744,29 @@ static DisasJumpType trans_dcor(DisasContext *ctx, uint32_t insn, } tcg_gen_andi_reg(tmp, tmp, 0x11111111); tcg_gen_muli_reg(tmp, tmp, 6); - ret = do_unit(ctx, rt, tmp, load_gpr(ctx, r2), cf, false, - is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); + do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, false, + is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); + return nullify_end(ctx); +} - return nullify_end(ctx, ret); +static bool trans_dcor(DisasContext *ctx, arg_rr_cf *a) +{ + return do_dcor(ctx, a, false); } -static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf *a) +{ + return do_dcor(ctx, a, true); +} + +static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); TCGv_reg dest, add1, add2, addc, zero, in1, in2; nullify_over(ctx); - in1 = load_gpr(ctx, r1); - in2 = load_gpr(ctx, r2); + in1 = load_gpr(ctx, a->r1); + in2 = load_gpr(ctx, a->r2); add1 = tcg_temp_new(); add2 = tcg_temp_new(); @@ -2795,7 +2793,7 @@ static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn, tcg_temp_free(zero); /* Write back the result register. */ - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Write back PSW[CB]. */ tcg_gen_xor_reg(cpu_psw_cb, add1, add2); @@ -2806,251 +2804,118 @@ static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn, tcg_gen_xor_reg(cpu_psw_v, cpu_psw_v, in2); /* Install the new nullification. */ - if (cf) { + if (a->cf) { TCGv_reg sv = NULL; - if (cf >> 1 == 6) { + if (cond_need_sv(a->cf >> 1)) { /* ??? The lshift is supposed to contribute to overflow. */ sv = do_add_sv(ctx, dest, add1, add2); } - ctx->null_cond = do_cond(cf, dest, cpu_psw_cb_msb, sv); + ctx->null_cond = do_cond(a->cf, dest, cpu_psw_cb_msb, sv); } tcg_temp_free(add1); tcg_temp_free(add2); tcg_temp_free(dest); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -#ifndef CONFIG_USER_ONLY -/* These are QEMU extensions and are nops in the real architecture: - * - * or %r10,%r10,%r10 -- idle loop; wait for interrupt - * or %r31,%r31,%r31 -- death loop; offline cpu - * currently implemented as idle. - */ -static DisasJumpType trans_pause(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_addi(DisasContext *ctx, arg_rri_cf *a) { - TCGv_i32 tmp; - - /* No need to check for supervisor, as userland can only pause - until the next timer interrupt. */ - nullify_over(ctx); - - /* Advance the instruction queue. */ - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); - nullify_set(ctx, 0); - - /* Tell the qemu main loop to halt until this cpu has work. */ - tmp = tcg_const_i32(1); - tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) + - offsetof(CPUState, halted)); - tcg_temp_free_i32(tmp); - gen_excp_1(EXCP_HALTED); - - return nullify_end(ctx, DISAS_NORETURN); + return do_add_imm(ctx, a, false, false); } -#endif -static const DisasInsn table_arith_log[] = { - { 0x08000240u, 0xfc00ffffu, trans_nop }, /* or x,y,0 */ - { 0x08000240u, 0xffe0ffe0u, trans_copy }, /* or x,0,t */ -#ifndef CONFIG_USER_ONLY - { 0x094a024au, 0xffffffffu, trans_pause }, /* or r10,r10,r10 */ - { 0x0bff025fu, 0xffffffffu, trans_pause }, /* or r31,r31,r31 */ -#endif - { 0x08000000u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_andc_reg }, - { 0x08000200u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_and_reg }, - { 0x08000240u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_or_reg }, - { 0x08000280u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_xor_reg }, - { 0x08000880u, 0xfc000fe0u, trans_cmpclr }, - { 0x08000380u, 0xfc000fe0u, trans_uxor }, - { 0x08000980u, 0xfc000fa0u, trans_uaddcm }, - { 0x08000b80u, 0xfc1f0fa0u, trans_dcor }, - { 0x08000440u, 0xfc000fe0u, trans_ds }, - { 0x08000700u, 0xfc0007e0u, trans_add }, /* add */ - { 0x08000400u, 0xfc0006e0u, trans_sub }, /* sub; sub,b; sub,tsv */ - { 0x080004c0u, 0xfc0007e0u, trans_sub }, /* sub,tc; sub,tsv,tc */ - { 0x08000200u, 0xfc000320u, trans_add }, /* shladd */ -}; - -static DisasJumpType trans_addi(DisasContext *ctx, uint32_t insn) +static bool trans_addi_tsv(DisasContext *ctx, arg_rri_cf *a) { - target_sreg im = low_sextract(insn, 0, 11); - unsigned e1 = extract32(insn, 11, 1); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - unsigned o1 = extract32(insn, 26, 1); - TCGv_reg tcg_im, tcg_r2; - DisasJumpType ret; - - if (cf) { - nullify_over(ctx); - } - - tcg_im = load_const(ctx, im); - tcg_r2 = load_gpr(ctx, r2); - ret = do_add(ctx, rt, tcg_im, tcg_r2, 0, false, e1, !o1, false, cf); - - return nullify_end(ctx, ret); + return do_add_imm(ctx, a, true, false); } -static DisasJumpType trans_subi(DisasContext *ctx, uint32_t insn) +static bool trans_addi_tc(DisasContext *ctx, arg_rri_cf *a) { - target_sreg im = low_sextract(insn, 0, 11); - unsigned e1 = extract32(insn, 11, 1); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - TCGv_reg tcg_im, tcg_r2; - DisasJumpType ret; + return do_add_imm(ctx, a, false, true); +} - if (cf) { - nullify_over(ctx); - } +static bool trans_addi_tc_tsv(DisasContext *ctx, arg_rri_cf *a) +{ + return do_add_imm(ctx, a, true, true); +} - tcg_im = load_const(ctx, im); - tcg_r2 = load_gpr(ctx, r2); - ret = do_sub(ctx, rt, tcg_im, tcg_r2, e1, false, false, cf); +static bool trans_subi(DisasContext *ctx, arg_rri_cf *a) +{ + return do_sub_imm(ctx, a, false); +} - return nullify_end(ctx, ret); +static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a) +{ + return do_sub_imm(ctx, a, true); } -static DisasJumpType trans_cmpiclr(DisasContext *ctx, uint32_t insn) +static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a) { - target_sreg im = low_sextract(insn, 0, 11); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); TCGv_reg tcg_im, tcg_r2; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, im); - tcg_r2 = load_gpr(ctx, r2); - ret = do_cmpclr(ctx, rt, tcg_im, tcg_r2, cf); + tcg_im = load_const(ctx, a->i); + tcg_r2 = load_gpr(ctx, a->r); + do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf); - return nullify_end(ctx, ret); + return nullify_end(ctx); } -static DisasJumpType trans_ld_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ld(DisasContext *ctx, arg_ldst *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sz = extract32(insn, 6, 2); - unsigned a = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - int disp = low_sextract(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - int modify = (m ? (a ? -1 : 1) : 0); - TCGMemOp mop = MO_TE | sz; - - return do_load(ctx, rt, rb, 0, 0, disp, sp, modify, mop); -} - -static DisasJumpType trans_ld_idx_x(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sz = extract32(insn, 6, 2); - unsigned u = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - TCGMemOp mop = MO_TE | sz; - - return do_load(ctx, rt, rb, rx, u ? sz : 0, 0, sp, m, mop); + return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, + a->disp, a->sp, a->m, a->size | MO_TE); } -static DisasJumpType trans_st_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_st(DisasContext *ctx, arg_ldst *a) { - int disp = low_sextract(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sz = extract32(insn, 6, 2); - unsigned a = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rr = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - int modify = (m ? (a ? -1 : 1) : 0); - TCGMemOp mop = MO_TE | sz; - - return do_store(ctx, rr, rb, disp, sp, modify, mop); + assert(a->x == 0 && a->scale == 0); + return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); } -static DisasJumpType trans_ldcw(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned i = extract32(insn, 12, 1); - unsigned au = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - TCGMemOp mop = MO_TEUL | MO_ALIGN_16; + TCGMemOp mop = MO_TEUL | MO_ALIGN_16 | a->size; TCGv_reg zero, dest, ofs; TCGv_tl addr; - int modify, disp = 0, scale = 0; nullify_over(ctx); - if (i) { - modify = (m ? (au ? -1 : 1) : 0); - disp = low_sextract(rx, 0, 5); - rx = 0; - } else { - modify = m; - if (au) { - scale = mop & MO_SIZE; - } - } - if (modify) { + if (a->m) { /* Base register modification. Make sure if RT == RB, we see the result of the load. */ dest = get_temp(ctx); } else { - dest = dest_gpr(ctx, rt); + dest = dest_gpr(ctx, a->t); } - form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + form_gva(ctx, &addr, &ofs, a->b, a->x, a->scale ? a->size : 0, + a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX); zero = tcg_const_reg(0); tcg_gen_atomic_xchg_reg(dest, addr, zero, ctx->mmu_idx, mop); - if (modify) { - save_gpr(ctx, rb, ofs); + if (a->m) { + save_gpr(ctx, a->b, ofs); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_stby(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_stby(DisasContext *ctx, arg_stby *a) { - target_sreg disp = low_sextract(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned a = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rt = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); TCGv_reg ofs, val; TCGv_tl addr; nullify_over(ctx); - form_gva(ctx, &addr, &ofs, rb, 0, 0, disp, sp, m, + form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX); - val = load_gpr(ctx, rt); - if (a) { + val = load_gpr(ctx, a->r); + if (a->a) { if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { gen_helper_stby_e_parallel(cpu_env, addr, val); } else { @@ -3063,433 +2928,222 @@ static DisasJumpType trans_stby(DisasContext *ctx, uint32_t insn, gen_helper_stby_b(cpu_env, addr, val); } } - - if (m) { + if (a->m) { tcg_gen_andi_reg(ofs, ofs, ~3); - save_gpr(ctx, rb, ofs); + save_gpr(ctx, a->b, ofs); } - return nullify_end(ctx, DISAS_NEXT); -} - -#ifndef CONFIG_USER_ONLY -static DisasJumpType trans_ldwa_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - int hold_mmu_idx = ctx->mmu_idx; - DisasJumpType ret; - - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - - /* ??? needs fixing for hppa64 -- ldda does not follow the same - format wrt the sub-opcode in bits 6:9. */ - ctx->mmu_idx = MMU_PHYS_IDX; - ret = trans_ld_idx_i(ctx, insn, di); - ctx->mmu_idx = hold_mmu_idx; - return ret; + return nullify_end(ctx); } -static DisasJumpType trans_ldwa_idx_x(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_lda(DisasContext *ctx, arg_ldst *a) { int hold_mmu_idx = ctx->mmu_idx; - DisasJumpType ret; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - - /* ??? needs fixing for hppa64 -- ldda does not follow the same - format wrt the sub-opcode in bits 6:9. */ ctx->mmu_idx = MMU_PHYS_IDX; - ret = trans_ld_idx_x(ctx, insn, di); + trans_ld(ctx, a); ctx->mmu_idx = hold_mmu_idx; - return ret; + return true; } -static DisasJumpType trans_stwa_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_sta(DisasContext *ctx, arg_ldst *a) { int hold_mmu_idx = ctx->mmu_idx; - DisasJumpType ret; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - - /* ??? needs fixing for hppa64 -- ldda does not follow the same - format wrt the sub-opcode in bits 6:9. */ ctx->mmu_idx = MMU_PHYS_IDX; - ret = trans_st_idx_i(ctx, insn, di); + trans_st(ctx, a); ctx->mmu_idx = hold_mmu_idx; - return ret; + return true; } -#endif -static const DisasInsn table_index_mem[] = { - { 0x0c001000u, 0xfc001300, trans_ld_idx_i }, /* LD[BHWD], im */ - { 0x0c000000u, 0xfc001300, trans_ld_idx_x }, /* LD[BHWD], rx */ - { 0x0c001200u, 0xfc001300, trans_st_idx_i }, /* ST[BHWD] */ - { 0x0c0001c0u, 0xfc0003c0, trans_ldcw }, - { 0x0c001300u, 0xfc0013c0, trans_stby }, -#ifndef CONFIG_USER_ONLY - { 0x0c000180u, 0xfc00d3c0, trans_ldwa_idx_x }, /* LDWA, rx */ - { 0x0c001180u, 0xfc00d3c0, trans_ldwa_idx_i }, /* LDWA, im */ - { 0x0c001380u, 0xfc00d3c0, trans_stwa_idx_i }, /* STWA, im */ -#endif -}; - -static DisasJumpType trans_ldil(DisasContext *ctx, uint32_t insn) +static bool trans_ldil(DisasContext *ctx, arg_ldil *a) { - unsigned rt = extract32(insn, 21, 5); - target_sreg i = assemble_21(insn); - TCGv_reg tcg_rt = dest_gpr(ctx, rt); + TCGv_reg tcg_rt = dest_gpr(ctx, a->t); - tcg_gen_movi_reg(tcg_rt, i); - save_gpr(ctx, rt, tcg_rt); + tcg_gen_movi_reg(tcg_rt, a->i); + save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); - - return DISAS_NEXT; + return true; } -static DisasJumpType trans_addil(DisasContext *ctx, uint32_t insn) +static bool trans_addil(DisasContext *ctx, arg_addil *a) { - unsigned rt = extract32(insn, 21, 5); - target_sreg i = assemble_21(insn); - TCGv_reg tcg_rt = load_gpr(ctx, rt); + TCGv_reg tcg_rt = load_gpr(ctx, a->r); TCGv_reg tcg_r1 = dest_gpr(ctx, 1); - tcg_gen_addi_reg(tcg_r1, tcg_rt, i); + tcg_gen_addi_reg(tcg_r1, tcg_rt, a->i); save_gpr(ctx, 1, tcg_r1); cond_free(&ctx->null_cond); - - return DISAS_NEXT; + return true; } -static DisasJumpType trans_ldo(DisasContext *ctx, uint32_t insn) +static bool trans_ldo(DisasContext *ctx, arg_ldo *a) { - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - target_sreg i = assemble_16(insn); - TCGv_reg tcg_rt = dest_gpr(ctx, rt); + TCGv_reg tcg_rt = dest_gpr(ctx, a->t); /* Special case rb == 0, for the LDI pseudo-op. The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */ - if (rb == 0) { - tcg_gen_movi_reg(tcg_rt, i); + if (a->b == 0) { + tcg_gen_movi_reg(tcg_rt, a->i); } else { - tcg_gen_addi_reg(tcg_rt, cpu_gr[rb], i); + tcg_gen_addi_reg(tcg_rt, cpu_gr[a->b], a->i); } - save_gpr(ctx, rt, tcg_rt); + save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); - - return DISAS_NEXT; + return true; } -static DisasJumpType trans_load(DisasContext *ctx, uint32_t insn, - bool is_mod, TCGMemOp mop) +static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, + unsigned c, unsigned f, unsigned n, int disp) { - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16(insn); + TCGv_reg dest, in2, sv; + DisasCond cond; - return do_load(ctx, rt, rb, 0, 0, i, sp, - is_mod ? (i < 0 ? -1 : 1) : 0, mop); -} + in2 = load_gpr(ctx, r); + dest = get_temp(ctx); -static DisasJumpType trans_load_w(DisasContext *ctx, uint32_t insn) -{ - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16a(insn); - unsigned ext2 = extract32(insn, 1, 2); + tcg_gen_sub_reg(dest, in1, in2); - switch (ext2) { - case 0: - case 1: - /* FLDW without modification. */ - return do_floadw(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0); - case 2: - /* LDW with modification. Note that the sign of I selects - post-dec vs pre-inc. */ - return do_load(ctx, rt, rb, 0, 0, i, sp, (i < 0 ? 1 : -1), MO_TEUL); - default: - return gen_illegal(ctx); + sv = NULL; + if (cond_need_sv(c)) { + sv = do_sub_sv(ctx, dest, in1, in2); } + + cond = do_sub_cond(c * 2 + f, dest, in1, in2, sv); + return do_cbranch(ctx, disp, n, &cond); } -static DisasJumpType trans_fload_mod(DisasContext *ctx, uint32_t insn) +static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a) { - target_sreg i = assemble_16a(insn); - unsigned t1 = extract32(insn, 1, 1); - unsigned a = extract32(insn, 2, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned t0 = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - - /* FLDW with modification. */ - return do_floadw(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1)); + nullify_over(ctx); + return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp); } -static DisasJumpType trans_store(DisasContext *ctx, uint32_t insn, - bool is_mod, TCGMemOp mop) +static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) { - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16(insn); - - return do_store(ctx, rt, rb, i, sp, is_mod ? (i < 0 ? -1 : 1) : 0, mop); + nullify_over(ctx); + return do_cmpb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); } -static DisasJumpType trans_store_w(DisasContext *ctx, uint32_t insn) +static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, + unsigned c, unsigned f, unsigned n, int disp) { - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16a(insn); - unsigned ext2 = extract32(insn, 1, 2); + TCGv_reg dest, in2, sv, cb_msb; + DisasCond cond; - switch (ext2) { - case 0: - case 1: - /* FSTW without modification. */ - return do_fstorew(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0); - case 2: - /* STW with modification. */ - return do_store(ctx, rt, rb, i, sp, (i < 0 ? 1 : -1), MO_TEUL); - default: - return gen_illegal(ctx); + in2 = load_gpr(ctx, r); + dest = dest_gpr(ctx, r); + sv = NULL; + cb_msb = NULL; + + if (cond_need_cb(c)) { + cb_msb = get_temp(ctx); + tcg_gen_movi_reg(cb_msb, 0); + tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); + } else { + tcg_gen_add_reg(dest, in1, in2); + } + if (cond_need_sv(c)) { + sv = do_add_sv(ctx, dest, in1, in2); } + + cond = do_cond(c * 2 + f, dest, cb_msb, sv); + return do_cbranch(ctx, disp, n, &cond); } -static DisasJumpType trans_fstore_mod(DisasContext *ctx, uint32_t insn) +static bool trans_addb(DisasContext *ctx, arg_addb *a) { - target_sreg i = assemble_16a(insn); - unsigned t1 = extract32(insn, 1, 1); - unsigned a = extract32(insn, 2, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned t0 = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - - /* FSTW with modification. */ - return do_fstorew(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1)); + nullify_over(ctx); + return do_addb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp); } -static DisasJumpType trans_copr_w(DisasContext *ctx, uint32_t insn) +static bool trans_addbi(DisasContext *ctx, arg_addbi *a) { - unsigned t0 = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned t1 = extract32(insn, 6, 1); - unsigned ext3 = extract32(insn, 7, 3); - /* unsigned cc = extract32(insn, 10, 2); */ - unsigned i = extract32(insn, 12, 1); - unsigned ua = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned rt = t1 * 32 + t0; - int modify = (m ? (ua ? -1 : 1) : 0); - int disp, scale; - - if (i == 0) { - scale = (ua ? 2 : 0); - disp = 0; - modify = m; - } else { - disp = low_sextract(rx, 0, 5); - scale = 0; - rx = 0; - modify = (m ? (ua ? -1 : 1) : 0); - } - - switch (ext3) { - case 0: /* FLDW */ - return do_floadw(ctx, rt, rb, rx, scale, disp, sp, modify); - case 4: /* FSTW */ - return do_fstorew(ctx, rt, rb, rx, scale, disp, sp, modify); - } - return gen_illegal(ctx); -} - -static DisasJumpType trans_copr_dw(DisasContext *ctx, uint32_t insn) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned ext4 = extract32(insn, 6, 4); - /* unsigned cc = extract32(insn, 10, 2); */ - unsigned i = extract32(insn, 12, 1); - unsigned ua = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - int modify = (m ? (ua ? -1 : 1) : 0); - int disp, scale; - - if (i == 0) { - scale = (ua ? 3 : 0); - disp = 0; - modify = m; - } else { - disp = low_sextract(rx, 0, 5); - scale = 0; - rx = 0; - modify = (m ? (ua ? -1 : 1) : 0); - } - - switch (ext4) { - case 0: /* FLDD */ - return do_floadd(ctx, rt, rb, rx, scale, disp, sp, modify); - case 8: /* FSTD */ - return do_fstored(ctx, rt, rb, rx, scale, disp, sp, modify); - default: - return gen_illegal(ctx); - } + nullify_over(ctx); + return do_addb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); } -static DisasJumpType trans_cmpb(DisasContext *ctx, uint32_t insn, - bool is_true, bool is_imm, bool is_dw) +static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 13, 3); - unsigned r = extract32(insn, 21, 5); - unsigned cf = c * 2 + !is_true; - TCGv_reg dest, in1, in2, sv; + TCGv_reg tmp, tcg_r; DisasCond cond; nullify_over(ctx); - if (is_imm) { - in1 = load_const(ctx, low_sextract(insn, 16, 5)); - } else { - in1 = load_gpr(ctx, extract32(insn, 16, 5)); - } - in2 = load_gpr(ctx, r); - dest = get_temp(ctx); - - tcg_gen_sub_reg(dest, in1, in2); - - sv = NULL; - if (c == 6) { - sv = do_sub_sv(ctx, dest, in1, in2); - } + tmp = tcg_temp_new(); + tcg_r = load_gpr(ctx, a->r); + tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); - cond = do_sub_cond(cf, dest, in1, in2, sv); - return do_cbranch(ctx, disp, n, &cond); + cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + tcg_temp_free(tmp); + return do_cbranch(ctx, a->disp, a->n, &cond); } -static DisasJumpType trans_addb(DisasContext *ctx, uint32_t insn, - bool is_true, bool is_imm) +static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 13, 3); - unsigned r = extract32(insn, 21, 5); - unsigned cf = c * 2 + !is_true; - TCGv_reg dest, in1, in2, sv, cb_msb; + TCGv_reg tmp, tcg_r; DisasCond cond; nullify_over(ctx); - if (is_imm) { - in1 = load_const(ctx, low_sextract(insn, 16, 5)); - } else { - in1 = load_gpr(ctx, extract32(insn, 16, 5)); - } - in2 = load_gpr(ctx, r); - dest = dest_gpr(ctx, r); - sv = NULL; - cb_msb = NULL; - - switch (c) { - default: - tcg_gen_add_reg(dest, in1, in2); - break; - case 4: case 5: - cb_msb = get_temp(ctx); - tcg_gen_movi_reg(cb_msb, 0); - tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); - break; - case 6: - tcg_gen_add_reg(dest, in1, in2); - sv = do_add_sv(ctx, dest, in1, in2); - break; - } + tmp = tcg_temp_new(); + tcg_r = load_gpr(ctx, a->r); + tcg_gen_shli_reg(tmp, tcg_r, a->p); - cond = do_cond(cf, dest, cb_msb, sv); - return do_cbranch(ctx, disp, n, &cond); + cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + tcg_temp_free(tmp); + return do_cbranch(ctx, a->disp, a->n, &cond); } -static DisasJumpType trans_bb(DisasContext *ctx, uint32_t insn) +static bool trans_movb(DisasContext *ctx, arg_movb *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 15, 1); - unsigned r = extract32(insn, 16, 5); - unsigned p = extract32(insn, 21, 5); - unsigned i = extract32(insn, 26, 1); - TCGv_reg tmp, tcg_r; + TCGv_reg dest; DisasCond cond; nullify_over(ctx); - tmp = tcg_temp_new(); - tcg_r = load_gpr(ctx, r); - if (i) { - tcg_gen_shli_reg(tmp, tcg_r, p); + dest = dest_gpr(ctx, a->r2); + if (a->r1 == 0) { + tcg_gen_movi_reg(dest, 0); } else { - tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); + tcg_gen_mov_reg(dest, cpu_gr[a->r1]); } - cond = cond_make_0(c ? TCG_COND_GE : TCG_COND_LT, tmp); - tcg_temp_free(tmp); - return do_cbranch(ctx, disp, n, &cond); + cond = do_sed_cond(a->c, dest); + return do_cbranch(ctx, a->disp, a->n, &cond); } -static DisasJumpType trans_movb(DisasContext *ctx, uint32_t insn, bool is_imm) +static bool trans_movbi(DisasContext *ctx, arg_movbi *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 13, 3); - unsigned t = extract32(insn, 16, 5); - unsigned r = extract32(insn, 21, 5); TCGv_reg dest; DisasCond cond; nullify_over(ctx); - dest = dest_gpr(ctx, r); - if (is_imm) { - tcg_gen_movi_reg(dest, low_sextract(t, 0, 5)); - } else if (t == 0) { - tcg_gen_movi_reg(dest, 0); - } else { - tcg_gen_mov_reg(dest, cpu_gr[t]); - } + dest = dest_gpr(ctx, a->r); + tcg_gen_movi_reg(dest, a->i); - cond = do_sed_cond(c, dest); - return do_cbranch(ctx, disp, n, &cond); + cond = do_sed_cond(a->c, dest); + return do_cbranch(ctx, a->disp, a->n, &cond); } -static DisasJumpType trans_shrpw_sar(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned c = extract32(insn, 13, 3); - unsigned r1 = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); TCGv_reg dest; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - if (r1 == 0) { - tcg_gen_ext32u_reg(dest, load_gpr(ctx, r2)); + dest = dest_gpr(ctx, a->t); + if (a->r1 == 0) { + tcg_gen_ext32u_reg(dest, load_gpr(ctx, a->r2)); tcg_gen_shr_reg(dest, dest, cpu_sar); - } else if (r1 == r2) { + } else if (a->r1 == a->r2) { TCGv_i32 t32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, r2)); + tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, a->r2)); tcg_gen_rotr_i32(t32, t32, cpu_sar); tcg_gen_extu_i32_reg(dest, t32); tcg_temp_free_i32(t32); @@ -3497,7 +3151,7 @@ static DisasJumpType trans_shrpw_sar(DisasContext *ctx, uint32_t insn, TCGv_i64 t = tcg_temp_new_i64(); TCGv_i64 s = tcg_temp_new_i64(); - tcg_gen_concat_reg_i64(t, load_gpr(ctx, r2), load_gpr(ctx, r1)); + tcg_gen_concat_reg_i64(t, load_gpr(ctx, a->r2), load_gpr(ctx, a->r1)); tcg_gen_extu_reg_i64(s, cpu_sar); tcg_gen_shr_i64(t, t, s); tcg_gen_trunc_i64_reg(dest, t); @@ -3505,79 +3159,67 @@ static DisasJumpType trans_shrpw_sar(DisasContext *ctx, uint32_t insn, tcg_temp_free_i64(t); tcg_temp_free_i64(s); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_shrpw_imm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned cpos = extract32(insn, 5, 5); - unsigned c = extract32(insn, 13, 3); - unsigned r1 = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - unsigned sa = 31 - cpos; + unsigned sa = 31 - a->cpos; TCGv_reg dest, t2; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - t2 = load_gpr(ctx, r2); - if (r1 == r2) { + dest = dest_gpr(ctx, a->t); + t2 = load_gpr(ctx, a->r2); + if (a->r1 == a->r2) { TCGv_i32 t32 = tcg_temp_new_i32(); tcg_gen_trunc_reg_i32(t32, t2); tcg_gen_rotri_i32(t32, t32, sa); tcg_gen_extu_i32_reg(dest, t32); tcg_temp_free_i32(t32); - } else if (r1 == 0) { + } else if (a->r1 == 0) { tcg_gen_extract_reg(dest, t2, sa, 32 - sa); } else { TCGv_reg t0 = tcg_temp_new(); tcg_gen_extract_reg(t0, t2, sa, 32 - sa); - tcg_gen_deposit_reg(dest, t0, cpu_gr[r1], 32 - sa, sa); + tcg_gen_deposit_reg(dest, t0, cpu_gr[a->r1], 32 - sa, sa); tcg_temp_free(t0); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_extrw_sar(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned is_se = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rt = extract32(insn, 16, 5); - unsigned rr = extract32(insn, 21, 5); - unsigned len = 32 - clen; + unsigned len = 32 - a->clen; TCGv_reg dest, src, tmp; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - src = load_gpr(ctx, rr); + dest = dest_gpr(ctx, a->t); + src = load_gpr(ctx, a->r); tmp = tcg_temp_new(); /* Recall that SAR is using big-endian bit numbering. */ tcg_gen_xori_reg(tmp, cpu_sar, TARGET_REGISTER_BITS - 1); - if (is_se) { + if (a->se) { tcg_gen_sar_reg(dest, src, tmp); tcg_gen_sextract_reg(dest, dest, 0, len); } else { @@ -3585,83 +3227,62 @@ static DisasJumpType trans_extrw_sar(DisasContext *ctx, uint32_t insn, tcg_gen_extract_reg(dest, dest, 0, len); } tcg_temp_free(tmp); - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_extrw_imm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned pos = extract32(insn, 5, 5); - unsigned is_se = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rt = extract32(insn, 16, 5); - unsigned rr = extract32(insn, 21, 5); - unsigned len = 32 - clen; - unsigned cpos = 31 - pos; + unsigned len = 32 - a->clen; + unsigned cpos = 31 - a->pos; TCGv_reg dest, src; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - src = load_gpr(ctx, rr); - if (is_se) { + dest = dest_gpr(ctx, a->t); + src = load_gpr(ctx, a->r); + if (a->se) { tcg_gen_sextract_reg(dest, src, cpos, len); } else { tcg_gen_extract_reg(dest, src, cpos, len); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static const DisasInsn table_sh_ex[] = { - { 0xd0000000u, 0xfc001fe0u, trans_shrpw_sar }, - { 0xd0000800u, 0xfc001c00u, trans_shrpw_imm }, - { 0xd0001000u, 0xfc001be0u, trans_extrw_sar }, - { 0xd0001800u, 0xfc001800u, trans_extrw_imm }, -}; - -static DisasJumpType trans_depw_imm_c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned cpos = extract32(insn, 5, 5); - unsigned nz = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - target_sreg val = low_sextract(insn, 16, 5); - unsigned rt = extract32(insn, 21, 5); - unsigned len = 32 - clen; + unsigned len = 32 - a->clen; target_sreg mask0, mask1; TCGv_reg dest; - if (c) { + if (a->c) { nullify_over(ctx); } - if (cpos + len > 32) { - len = 32 - cpos; + if (a->cpos + len > 32) { + len = 32 - a->cpos; } - dest = dest_gpr(ctx, rt); - mask0 = deposit64(0, cpos, len, val); - mask1 = deposit64(-1, cpos, len, val); + dest = dest_gpr(ctx, a->t); + mask0 = deposit64(0, a->cpos, len, a->i); + mask1 = deposit64(-1, a->cpos, len, a->i); - if (nz) { - TCGv_reg src = load_gpr(ctx, rt); + if (a->nz) { + TCGv_reg src = load_gpr(ctx, a->t); if (mask1 != -1) { tcg_gen_andi_reg(dest, src, mask1); src = dest; @@ -3670,75 +3291,58 @@ static DisasJumpType trans_depw_imm_c(DisasContext *ctx, uint32_t insn, } else { tcg_gen_movi_reg(dest, mask0); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_depw_imm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned cpos = extract32(insn, 5, 5); - unsigned nz = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rr = extract32(insn, 16, 5); - unsigned rt = extract32(insn, 21, 5); - unsigned rs = nz ? rt : 0; - unsigned len = 32 - clen; + unsigned rs = a->nz ? a->t : 0; + unsigned len = 32 - a->clen; TCGv_reg dest, val; - if (c) { + if (a->c) { nullify_over(ctx); } - if (cpos + len > 32) { - len = 32 - cpos; + if (a->cpos + len > 32) { + len = 32 - a->cpos; } - dest = dest_gpr(ctx, rt); - val = load_gpr(ctx, rr); + dest = dest_gpr(ctx, a->t); + val = load_gpr(ctx, a->r); if (rs == 0) { - tcg_gen_deposit_z_reg(dest, val, cpos, len); + tcg_gen_deposit_z_reg(dest, val, a->cpos, len); } else { - tcg_gen_deposit_reg(dest, cpu_gr[rs], val, cpos, len); + tcg_gen_deposit_reg(dest, cpu_gr[rs], val, a->cpos, len); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_depw_sar(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, + unsigned nz, unsigned clen, TCGv_reg val) { - unsigned clen = extract32(insn, 0, 5); - unsigned nz = extract32(insn, 10, 1); - unsigned i = extract32(insn, 12, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rt = extract32(insn, 21, 5); unsigned rs = nz ? rt : 0; unsigned len = 32 - clen; - TCGv_reg val, mask, tmp, shift, dest; + TCGv_reg mask, tmp, shift, dest; unsigned msb = 1U << (len - 1); if (c) { nullify_over(ctx); } - if (i) { - val = load_const(ctx, low_sextract(insn, 16, 5)); - } else { - val = load_gpr(ctx, extract32(insn, 16, 5)); - } dest = dest_gpr(ctx, rt); shift = tcg_temp_new(); tmp = tcg_temp_new(); @@ -3766,20 +3370,21 @@ static DisasJumpType trans_depw_sar(DisasContext *ctx, uint32_t insn, if (c) { ctx->null_cond = do_sed_cond(c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static const DisasInsn table_depw[] = { - { 0xd4000000u, 0xfc000be0u, trans_depw_sar }, - { 0xd4000800u, 0xfc001800u, trans_depw_imm }, - { 0xd4001800u, 0xfc001800u, trans_depw_imm_c }, -}; +static bool trans_depw_sar(DisasContext *ctx, arg_depw_sar *a) +{ + return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_gpr(ctx, a->r)); +} -static DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l) +static bool trans_depwi_sar(DisasContext *ctx, arg_depwi_sar *a) +{ + return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_const(ctx, a->i)); +} + +static bool trans_be(DisasContext *ctx, arg_be *a) { - unsigned n = extract32(insn, 1, 1); - unsigned b = extract32(insn, 21, 5); - target_sreg disp = assemble_17(insn); TCGv_reg tmp; #ifdef CONFIG_USER_ONLY @@ -3791,29 +3396,28 @@ static DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l) /* Since we don't implement spaces, just branch. Do notice the special case of "be disp(*,r0)" using a direct branch to disp, so that we can goto_tb to the TB containing the syscall. */ - if (b == 0) { - return do_dbranch(ctx, disp, is_l ? 31 : 0, n); + if (a->b == 0) { + return do_dbranch(ctx, a->disp, a->l, a->n); } #else - int sp = assemble_sr3(insn); nullify_over(ctx); #endif tmp = get_temp(ctx); - tcg_gen_addi_reg(tmp, load_gpr(ctx, b), disp); + tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); #ifdef CONFIG_USER_ONLY - return do_ibranch(ctx, tmp, is_l ? 31 : 0, n); + return do_ibranch(ctx, tmp, a->l, a->n); #else TCGv_i64 new_spc = tcg_temp_new_i64(); - load_spr(ctx, new_spc, sp); - if (is_l) { + load_spr(ctx, new_spc, a->sp); + if (a->l) { copy_iaoq_entry(cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f); } - if (n && use_nullify_skip(ctx)) { + if (a->n && use_nullify_skip(ctx)) { tcg_gen_mov_reg(cpu_iaoq_f, tmp); tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); tcg_gen_mov_i64(cpu_iasq_f, new_spc); @@ -3825,31 +3429,23 @@ static DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l) } tcg_gen_mov_reg(cpu_iaoq_b, tmp); tcg_gen_mov_i64(cpu_iasq_b, new_spc); - nullify_set(ctx, n); + nullify_set(ctx, a->n); } tcg_temp_free_i64(new_spc); tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx, DISAS_NORETURN); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); #endif } -static DisasJumpType trans_bl(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_bl(DisasContext *ctx, arg_bl *a) { - unsigned n = extract32(insn, 1, 1); - unsigned link = extract32(insn, 21, 5); - target_sreg disp = assemble_17(insn); - - return do_dbranch(ctx, iaoq_dest(ctx, disp), link, n); + return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n); } -static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { - unsigned n = extract32(insn, 1, 1); - unsigned link = extract32(insn, 21, 5); - target_sreg disp = assemble_17(insn); - target_ureg dest = iaoq_dest(ctx, disp); + target_ureg dest = iaoq_dest(ctx, a->disp); /* Make sure the caller hasn't done something weird with the queue. * ??? This is not quite the same as the PSW[B] bit, which would be @@ -3876,7 +3472,8 @@ static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn, we will re-translate, at which point we *will* be able to find the TLB entry and determine if this is in fact a gateway page. */ if (type < 0) { - return gen_excp(ctx, EXCP_ITLB_MISS); + gen_excp(ctx, EXCP_ITLB_MISS); + return true; } /* No change for non-gateway pages or for priv decrease. */ if (type >= 4 && type - 4 < ctx->privilege) { @@ -3887,65 +3484,44 @@ static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn, } #endif - return do_dbranch(ctx, dest, link, n); + return do_dbranch(ctx, dest, a->l, a->n); } -static DisasJumpType trans_bl_long(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_blr(DisasContext *ctx, arg_blr *a) { - unsigned n = extract32(insn, 1, 1); - target_sreg disp = assemble_22(insn); - - return do_dbranch(ctx, iaoq_dest(ctx, disp), 2, n); -} - -static DisasJumpType trans_blr(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned n = extract32(insn, 1, 1); - unsigned rx = extract32(insn, 16, 5); - unsigned link = extract32(insn, 21, 5); TCGv_reg tmp = get_temp(ctx); - tcg_gen_shli_reg(tmp, load_gpr(ctx, rx), 3); + tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3); tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ - return do_ibranch(ctx, tmp, link, n); + return do_ibranch(ctx, tmp, a->l, a->n); } -static DisasJumpType trans_bv(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_bv(DisasContext *ctx, arg_bv *a) { - unsigned n = extract32(insn, 1, 1); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); TCGv_reg dest; - if (rx == 0) { - dest = load_gpr(ctx, rb); + if (a->x == 0) { + dest = load_gpr(ctx, a->b); } else { dest = get_temp(ctx); - tcg_gen_shli_reg(dest, load_gpr(ctx, rx), 3); - tcg_gen_add_reg(dest, dest, load_gpr(ctx, rb)); + tcg_gen_shli_reg(dest, load_gpr(ctx, a->x), 3); + tcg_gen_add_reg(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); - return do_ibranch(ctx, dest, 0, n); + return do_ibranch(ctx, dest, 0, a->n); } -static DisasJumpType trans_bve(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_bve(DisasContext *ctx, arg_bve *a) { - unsigned n = extract32(insn, 1, 1); - unsigned rb = extract32(insn, 21, 5); - unsigned link = extract32(insn, 13, 1) ? 2 : 0; TCGv_reg dest; #ifdef CONFIG_USER_ONLY - dest = do_ibranch_priv(ctx, load_gpr(ctx, rb)); - return do_ibranch(ctx, dest, link, n); + dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); + return do_ibranch(ctx, dest, a->l, a->n); #else nullify_over(ctx); - dest = do_ibranch_priv(ctx, load_gpr(ctx, rb)); + dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { @@ -3953,158 +3529,268 @@ static DisasJumpType trans_bve(DisasContext *ctx, uint32_t insn, } copy_iaoq_entry(cpu_iaoq_b, -1, dest); tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); - if (link) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + if (a->l) { + copy_iaoq_entry(cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); } - nullify_set(ctx, n); + nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx, DISAS_NORETURN); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); #endif } -static const DisasInsn table_branch[] = { - { 0xe8000000u, 0xfc006000u, trans_bl }, /* B,L and B,L,PUSH */ - { 0xe800a000u, 0xfc00e000u, trans_bl_long }, - { 0xe8004000u, 0xfc00fffdu, trans_blr }, - { 0xe800c000u, 0xfc00fffdu, trans_bv }, - { 0xe800d000u, 0xfc00dffcu, trans_bve }, - { 0xe8002000u, 0xfc00e000u, trans_b_gate }, -}; +/* + * Float class 0 + */ -static DisasJumpType trans_fop_wew_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static void gen_fcpy_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_wew(ctx, rt, ra, di->f.wew); + tcg_gen_mov_i32(dst, src); } -static DisasJumpType trans_fop_wew_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fcpy_f(DisasContext *ctx, arg_fclass01 *a) { - unsigned rt = assemble_rt64(insn); - unsigned ra = assemble_ra64(insn); - return do_fop_wew(ctx, rt, ra, di->f.wew); + return do_fop_wew(ctx, a->t, a->r, gen_fcpy_f); } -static DisasJumpType trans_fop_ded(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_ded(ctx, rt, ra, di->f.ded); + tcg_gen_mov_i64(dst, src); } -static DisasJumpType trans_fop_wed_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fcpy_d(DisasContext *ctx, arg_fclass01 *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_wed(ctx, rt, ra, di->f.wed); + return do_fop_ded(ctx, a->t, a->r, gen_fcpy_d); } -static DisasJumpType trans_fop_wed_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static void gen_fabs_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { - unsigned rt = assemble_rt64(insn); - unsigned ra = extract32(insn, 21, 5); - return do_fop_wed(ctx, rt, ra, di->f.wed); + tcg_gen_andi_i32(dst, src, INT32_MAX); } -static DisasJumpType trans_fop_dew_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fabs_f(DisasContext *ctx, arg_fclass01 *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_dew(ctx, rt, ra, di->f.dew); + return do_fop_wew(ctx, a->t, a->r, gen_fabs_f); } -static DisasJumpType trans_fop_dew_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { - unsigned rt = extract32(insn, 0, 5); - unsigned ra = assemble_ra64(insn); - return do_fop_dew(ctx, rt, ra, di->f.dew); + tcg_gen_andi_i64(dst, src, INT64_MAX); } -static DisasJumpType trans_fop_weww_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fabs_d(DisasContext *ctx, arg_fclass01 *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_weww(ctx, rt, ra, rb, di->f.weww); + return do_fop_ded(ctx, a->t, a->r, gen_fabs_d); } -static DisasJumpType trans_fop_weww_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fsqrt_f(DisasContext *ctx, arg_fclass01 *a) { - unsigned rt = assemble_rt64(insn); - unsigned rb = assemble_rb64(insn); - unsigned ra = assemble_ra64(insn); - return do_fop_weww(ctx, rt, ra, rb, di->f.weww); + return do_fop_wew(ctx, a->t, a->r, gen_helper_fsqrt_s); } -static DisasJumpType trans_fop_dedd(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fsqrt_d(DisasContext *ctx, arg_fclass01 *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_dedd(ctx, rt, ra, rb, di->f.dedd); + return do_fop_ded(ctx, a->t, a->r, gen_helper_fsqrt_d); } -static void gen_fcpy_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static bool trans_frnd_f(DisasContext *ctx, arg_fclass01 *a) { - tcg_gen_mov_i32(dst, src); + return do_fop_wew(ctx, a->t, a->r, gen_helper_frnd_s); } -static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +static bool trans_frnd_d(DisasContext *ctx, arg_fclass01 *a) { - tcg_gen_mov_i64(dst, src); + return do_fop_ded(ctx, a->t, a->r, gen_helper_frnd_d); } -static void gen_fabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static void gen_fneg_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { - tcg_gen_andi_i32(dst, src, INT32_MAX); + tcg_gen_xori_i32(dst, src, INT32_MIN); } -static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +static bool trans_fneg_f(DisasContext *ctx, arg_fclass01 *a) { - tcg_gen_andi_i64(dst, src, INT64_MAX); + return do_fop_wew(ctx, a->t, a->r, gen_fneg_f); } -static void gen_fneg_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { - tcg_gen_xori_i32(dst, src, INT32_MIN); + tcg_gen_xori_i64(dst, src, INT64_MIN); } -static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +static bool trans_fneg_d(DisasContext *ctx, arg_fclass01 *a) { - tcg_gen_xori_i64(dst, src, INT64_MIN); + return do_fop_ded(ctx, a->t, a->r, gen_fneg_d); } -static void gen_fnegabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static void gen_fnegabs_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { tcg_gen_ori_i32(dst, src, INT32_MIN); } +static bool trans_fnegabs_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_fnegabs_f); +} + static void gen_fnegabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { tcg_gen_ori_i64(dst, src, INT64_MIN); } -static DisasJumpType do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb, - unsigned y, unsigned c) +static bool trans_fnegabs_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_fnegabs_d); +} + +/* + * Float class 1 + */ + +static bool trans_fcnv_d_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_s); +} + +static bool trans_fcnv_f_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_d); +} + +static bool trans_fcnv_w_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_w_s); +} + +static bool trans_fcnv_q_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_dw_s); +} + +static bool trans_fcnv_w_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_w_d); +} + +static bool trans_fcnv_q_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_dw_d); +} + +static bool trans_fcnv_f_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_w); +} + +static bool trans_fcnv_d_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_w); +} + +static bool trans_fcnv_f_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_dw); +} + +static bool trans_fcnv_d_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_d_dw); +} + +static bool trans_fcnv_t_f_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_t_s_w); +} + +static bool trans_fcnv_t_d_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_t_d_w); +} + +static bool trans_fcnv_t_f_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_t_s_dw); +} + +static bool trans_fcnv_t_d_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_t_d_dw); +} + +static bool trans_fcnv_uw_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_uw_s); +} + +static bool trans_fcnv_uq_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_udw_s); +} + +static bool trans_fcnv_uw_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_uw_d); +} + +static bool trans_fcnv_uq_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_udw_d); +} + +static bool trans_fcnv_f_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_uw); +} + +static bool trans_fcnv_d_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_uw); +} + +static bool trans_fcnv_f_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_udw); +} + +static bool trans_fcnv_d_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_d_udw); +} + +static bool trans_fcnv_t_f_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_t_s_uw); +} + +static bool trans_fcnv_t_d_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_t_d_uw); +} + +static bool trans_fcnv_t_f_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_t_s_udw); +} + +static bool trans_fcnv_t_d_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_t_d_udw); +} + +/* + * Float class 2 + */ + +static bool trans_fcmp_f(DisasContext *ctx, arg_fclass2 *a) { TCGv_i32 ta, tb, tc, ty; nullify_over(ctx); - ta = load_frw0_i32(ra); - tb = load_frw0_i32(rb); - ty = tcg_const_i32(y); - tc = tcg_const_i32(c); + ta = load_frw0_i32(a->r1); + tb = load_frw0_i32(a->r2); + ty = tcg_const_i32(a->y); + tc = tcg_const_i32(a->c); gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc); @@ -4113,45 +3799,20 @@ static DisasJumpType do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb, tcg_temp_free_i32(ty); tcg_temp_free_i32(tc); - return nullify_end(ctx, DISAS_NEXT); -} - -static DisasJumpType trans_fcmp_s_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned c = extract32(insn, 0, 5); - unsigned y = extract32(insn, 13, 3); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fcmp_s(ctx, ra, rb, y, c); + return nullify_end(ctx); } -static DisasJumpType trans_fcmp_s_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a) { - unsigned c = extract32(insn, 0, 5); - unsigned y = extract32(insn, 13, 3); - unsigned rb = assemble_rb64(insn); - unsigned ra = assemble_ra64(insn); - return do_fcmp_s(ctx, ra, rb, y, c); -} - -static DisasJumpType trans_fcmp_d(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned c = extract32(insn, 0, 5); - unsigned y = extract32(insn, 13, 3); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); TCGv_i64 ta, tb; TCGv_i32 tc, ty; nullify_over(ctx); - ta = load_frd0(ra); - tb = load_frd0(rb); - ty = tcg_const_i32(y); - tc = tcg_const_i32(c); + ta = load_frd0(a->r1); + tb = load_frd0(a->r2); + ty = tcg_const_i32(a->y); + tc = tcg_const_i32(a->c); gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc); @@ -4160,266 +3821,131 @@ static DisasJumpType trans_fcmp_d(DisasContext *ctx, uint32_t insn, tcg_temp_free_i32(ty); tcg_temp_free_i32(tc); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_ftest_t(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ftest(DisasContext *ctx, arg_ftest *a) { - unsigned y = extract32(insn, 13, 3); - unsigned cbit = (y ^ 1) - 1; TCGv_reg t; nullify_over(ctx); - t = tcg_temp_new(); + t = get_temp(ctx); tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); - tcg_gen_extract_reg(t, t, 21 - cbit, 1); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); - tcg_temp_free(t); - return nullify_end(ctx, DISAS_NEXT); + if (a->y == 1) { + int mask; + bool inv = false; + + switch (a->c) { + case 0: /* simple */ + tcg_gen_andi_reg(t, t, 0x4000000); + ctx->null_cond = cond_make_0(TCG_COND_NE, t); + goto done; + case 2: /* rej */ + inv = true; + /* fallthru */ + case 1: /* acc */ + mask = 0x43ff800; + break; + case 6: /* rej8 */ + inv = true; + /* fallthru */ + case 5: /* acc8 */ + mask = 0x43f8000; + break; + case 9: /* acc6 */ + mask = 0x43e0000; + break; + case 13: /* acc4 */ + mask = 0x4380000; + break; + case 17: /* acc2 */ + mask = 0x4200000; + break; + default: + gen_illegal(ctx); + return true; + } + if (inv) { + TCGv_reg c = load_const(ctx, mask); + tcg_gen_or_reg(t, t, c); + ctx->null_cond = cond_make(TCG_COND_EQ, t, c); + } else { + tcg_gen_andi_reg(t, t, mask); + ctx->null_cond = cond_make_0(TCG_COND_EQ, t); + } + } else { + unsigned cbit = (a->y ^ 1) - 1; + + tcg_gen_extract_reg(t, t, 21 - cbit, 1); + ctx->null_cond = cond_make_0(TCG_COND_NE, t); + tcg_temp_free(t); + } + + done: + return nullify_end(ctx); } -static DisasJumpType trans_ftest_q(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +/* + * Float class 2 + */ + +static bool trans_fadd_f(DisasContext *ctx, arg_fclass3 *a) { - unsigned c = extract32(insn, 0, 5); - int mask; - bool inv = false; - TCGv_reg t; + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fadd_s); +} - nullify_over(ctx); +static bool trans_fadd_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fadd_d); +} - t = tcg_temp_new(); - tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); +static bool trans_fsub_f(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fsub_s); +} - switch (c) { - case 0: /* simple */ - tcg_gen_andi_reg(t, t, 0x4000000); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); - goto done; - case 2: /* rej */ - inv = true; - /* fallthru */ - case 1: /* acc */ - mask = 0x43ff800; - break; - case 6: /* rej8 */ - inv = true; - /* fallthru */ - case 5: /* acc8 */ - mask = 0x43f8000; - break; - case 9: /* acc6 */ - mask = 0x43e0000; - break; - case 13: /* acc4 */ - mask = 0x4380000; - break; - case 17: /* acc2 */ - mask = 0x4200000; - break; - default: - return gen_illegal(ctx); - } - if (inv) { - TCGv_reg c = load_const(ctx, mask); - tcg_gen_or_reg(t, t, c); - ctx->null_cond = cond_make(TCG_COND_EQ, t, c); - } else { - tcg_gen_andi_reg(t, t, mask); - ctx->null_cond = cond_make_0(TCG_COND_EQ, t); - } - done: - return nullify_end(ctx, DISAS_NEXT); +static bool trans_fsub_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fsub_d); } -static DisasJumpType trans_xmpyu(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fmpy_f(DisasContext *ctx, arg_fclass3 *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned rb = assemble_rb64(insn); - unsigned ra = assemble_ra64(insn); - TCGv_i64 a, b; + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fmpy_s); +} - nullify_over(ctx); +static bool trans_fmpy_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fmpy_d); +} - a = load_frw0_i64(ra); - b = load_frw0_i64(rb); - tcg_gen_mul_i64(a, a, b); - save_frd(rt, a); - tcg_temp_free_i64(a); - tcg_temp_free_i64(b); +static bool trans_fdiv_f(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_s); +} - return nullify_end(ctx, DISAS_NEXT); -} - -#define FOP_DED trans_fop_ded, .f.ded -#define FOP_DEDD trans_fop_dedd, .f.dedd - -#define FOP_WEW trans_fop_wew_0c, .f.wew -#define FOP_DEW trans_fop_dew_0c, .f.dew -#define FOP_WED trans_fop_wed_0c, .f.wed -#define FOP_WEWW trans_fop_weww_0c, .f.weww - -static const DisasInsn table_float_0c[] = { - /* floating point class zero */ - { 0x30004000, 0xfc1fffe0, FOP_WEW = gen_fcpy_s }, - { 0x30006000, 0xfc1fffe0, FOP_WEW = gen_fabs_s }, - { 0x30008000, 0xfc1fffe0, FOP_WEW = gen_helper_fsqrt_s }, - { 0x3000a000, 0xfc1fffe0, FOP_WEW = gen_helper_frnd_s }, - { 0x3000c000, 0xfc1fffe0, FOP_WEW = gen_fneg_s }, - { 0x3000e000, 0xfc1fffe0, FOP_WEW = gen_fnegabs_s }, - - { 0x30004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d }, - { 0x30006800, 0xfc1fffe0, FOP_DED = gen_fabs_d }, - { 0x30008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d }, - { 0x3000a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d }, - { 0x3000c800, 0xfc1fffe0, FOP_DED = gen_fneg_d }, - { 0x3000e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d }, - - /* floating point class three */ - { 0x30000600, 0xfc00ffe0, FOP_WEWW = gen_helper_fadd_s }, - { 0x30002600, 0xfc00ffe0, FOP_WEWW = gen_helper_fsub_s }, - { 0x30004600, 0xfc00ffe0, FOP_WEWW = gen_helper_fmpy_s }, - { 0x30006600, 0xfc00ffe0, FOP_WEWW = gen_helper_fdiv_s }, - - { 0x30000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d }, - { 0x30002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d }, - { 0x30004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d }, - { 0x30006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d }, - - /* floating point class one */ - /* float/float */ - { 0x30000a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_s }, - { 0x30002200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_d }, - /* int/float */ - { 0x30008200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_w_s }, - { 0x30008a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_dw_s }, - { 0x3000a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_w_d }, - { 0x3000aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d }, - /* float/int */ - { 0x30010200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_w }, - { 0x30010a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_w }, - { 0x30012200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_dw }, - { 0x30012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw }, - /* float/int truncate */ - { 0x30018200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_w }, - { 0x30018a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_w }, - { 0x3001a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_dw }, - { 0x3001aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw }, - /* uint/float */ - { 0x30028200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_uw_s }, - { 0x30028a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_udw_s }, - { 0x3002a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_uw_d }, - { 0x3002aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d }, - /* float/uint */ - { 0x30030200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_uw }, - { 0x30030a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_uw }, - { 0x30032200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_udw }, - { 0x30032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw }, - /* float/uint truncate */ - { 0x30038200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_uw }, - { 0x30038a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_uw }, - { 0x3003a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_udw }, - { 0x3003aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw }, - - /* floating point class two */ - { 0x30000400, 0xfc001fe0, trans_fcmp_s_0c }, - { 0x30000c00, 0xfc001fe0, trans_fcmp_d }, - { 0x30002420, 0xffffffe0, trans_ftest_q }, - { 0x30000420, 0xffff1fff, trans_ftest_t }, - - /* FID. Note that ra == rt == 0, which via fcpy puts 0 into fr0. - This is machine/revision == 0, which is reserved for simulator. */ - { 0x30000000, 0xffffffff, FOP_WEW = gen_fcpy_s }, -}; +static bool trans_fdiv_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_d); +} -#undef FOP_WEW -#undef FOP_DEW -#undef FOP_WED -#undef FOP_WEWW -#define FOP_WEW trans_fop_wew_0e, .f.wew -#define FOP_DEW trans_fop_dew_0e, .f.dew -#define FOP_WED trans_fop_wed_0e, .f.wed -#define FOP_WEWW trans_fop_weww_0e, .f.weww - -static const DisasInsn table_float_0e[] = { - /* floating point class zero */ - { 0x38004000, 0xfc1fff20, FOP_WEW = gen_fcpy_s }, - { 0x38006000, 0xfc1fff20, FOP_WEW = gen_fabs_s }, - { 0x38008000, 0xfc1fff20, FOP_WEW = gen_helper_fsqrt_s }, - { 0x3800a000, 0xfc1fff20, FOP_WEW = gen_helper_frnd_s }, - { 0x3800c000, 0xfc1fff20, FOP_WEW = gen_fneg_s }, - { 0x3800e000, 0xfc1fff20, FOP_WEW = gen_fnegabs_s }, - - { 0x38004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d }, - { 0x38006800, 0xfc1fffe0, FOP_DED = gen_fabs_d }, - { 0x38008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d }, - { 0x3800a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d }, - { 0x3800c800, 0xfc1fffe0, FOP_DED = gen_fneg_d }, - { 0x3800e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d }, - - /* floating point class three */ - { 0x38000600, 0xfc00ef20, FOP_WEWW = gen_helper_fadd_s }, - { 0x38002600, 0xfc00ef20, FOP_WEWW = gen_helper_fsub_s }, - { 0x38004600, 0xfc00ef20, FOP_WEWW = gen_helper_fmpy_s }, - { 0x38006600, 0xfc00ef20, FOP_WEWW = gen_helper_fdiv_s }, - - { 0x38000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d }, - { 0x38002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d }, - { 0x38004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d }, - { 0x38006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d }, - - { 0x38004700, 0xfc00ef60, trans_xmpyu }, - - /* floating point class one */ - /* float/float */ - { 0x38000a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_s }, - { 0x38002200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_d }, - /* int/float */ - { 0x38008200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_w_s }, - { 0x38008a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_dw_s }, - { 0x3800a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_w_d }, - { 0x3800aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d }, - /* float/int */ - { 0x38010200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_w }, - { 0x38010a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_w }, - { 0x38012200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_dw }, - { 0x38012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw }, - /* float/int truncate */ - { 0x38018200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_w }, - { 0x38018a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_w }, - { 0x3801a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_dw }, - { 0x3801aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw }, - /* uint/float */ - { 0x38028200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_uw_s }, - { 0x38028a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_udw_s }, - { 0x3802a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_uw_d }, - { 0x3802aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d }, - /* float/uint */ - { 0x38030200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_uw }, - { 0x38030a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_uw }, - { 0x38032200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_udw }, - { 0x38032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw }, - /* float/uint truncate */ - { 0x38038200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_uw }, - { 0x38038a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_uw }, - { 0x3803a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_udw }, - { 0x3803aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw }, - - /* floating point class two */ - { 0x38000400, 0xfc000f60, trans_fcmp_s_0e }, - { 0x38000c00, 0xfc001fe0, trans_fcmp_d }, -}; +static bool trans_xmpyu(DisasContext *ctx, arg_xmpyu *a) +{ + TCGv_i64 x, y; -#undef FOP_WEW -#undef FOP_DEW -#undef FOP_WED -#undef FOP_WEWW -#undef FOP_DED -#undef FOP_DEDD + nullify_over(ctx); + + x = load_frw0_i64(a->r1); + y = load_frw0_i64(a->r2); + tcg_gen_mul_i64(x, x, y); + save_frd(a->t, x); + tcg_temp_free_i64(x); + tcg_temp_free_i64(y); + + return nullify_end(ctx); +} /* Convert the fmpyadd single-precision register encodings to standard. */ static inline int fmpyadd_s_reg(unsigned r) @@ -4427,246 +3953,96 @@ static inline int fmpyadd_s_reg(unsigned r) return (r & 16) * 2 + 16 + (r & 15); } -static DisasJumpType trans_fmpyadd(DisasContext *ctx, - uint32_t insn, bool is_sub) +static bool do_fmpyadd_s(DisasContext *ctx, arg_mpyadd *a, bool is_sub) { - unsigned tm = extract32(insn, 0, 5); - unsigned f = extract32(insn, 5, 1); - unsigned ra = extract32(insn, 6, 5); - unsigned ta = extract32(insn, 11, 5); - unsigned rm2 = extract32(insn, 16, 5); - unsigned rm1 = extract32(insn, 21, 5); + int tm = fmpyadd_s_reg(a->tm); + int ra = fmpyadd_s_reg(a->ra); + int ta = fmpyadd_s_reg(a->ta); + int rm2 = fmpyadd_s_reg(a->rm2); + int rm1 = fmpyadd_s_reg(a->rm1); nullify_over(ctx); - /* Independent multiply & add/sub, with undefined behaviour - if outputs overlap inputs. */ - if (f == 0) { - tm = fmpyadd_s_reg(tm); - ra = fmpyadd_s_reg(ra); - ta = fmpyadd_s_reg(ta); - rm2 = fmpyadd_s_reg(rm2); - rm1 = fmpyadd_s_reg(rm1); - do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s); - do_fop_weww(ctx, ta, ta, ra, - is_sub ? gen_helper_fsub_s : gen_helper_fadd_s); - } else { - do_fop_dedd(ctx, tm, rm1, rm2, gen_helper_fmpy_d); - do_fop_dedd(ctx, ta, ta, ra, - is_sub ? gen_helper_fsub_d : gen_helper_fadd_d); - } + do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s); + do_fop_weww(ctx, ta, ta, ra, + is_sub ? gen_helper_fsub_s : gen_helper_fadd_s); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_fmpyfadd_s(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fmpyadd_f(DisasContext *ctx, arg_mpyadd *a) { - unsigned rt = assemble_rt64(insn); - unsigned neg = extract32(insn, 5, 1); - unsigned rm1 = assemble_ra64(insn); - unsigned rm2 = assemble_rb64(insn); - unsigned ra3 = assemble_rc64(insn); - TCGv_i32 a, b, c; + return do_fmpyadd_s(ctx, a, false); +} + +static bool trans_fmpysub_f(DisasContext *ctx, arg_mpyadd *a) +{ + return do_fmpyadd_s(ctx, a, true); +} +static bool do_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a, bool is_sub) +{ nullify_over(ctx); - a = load_frw0_i32(rm1); - b = load_frw0_i32(rm2); - c = load_frw0_i32(ra3); - if (neg) { - gen_helper_fmpynfadd_s(a, cpu_env, a, b, c); - } else { - gen_helper_fmpyfadd_s(a, cpu_env, a, b, c); - } + do_fop_dedd(ctx, a->tm, a->rm1, a->rm2, gen_helper_fmpy_d); + do_fop_dedd(ctx, a->ta, a->ta, a->ra, + is_sub ? gen_helper_fsub_d : gen_helper_fadd_d); - tcg_temp_free_i32(b); - tcg_temp_free_i32(c); - save_frw_i32(rt, a); - tcg_temp_free_i32(a); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); +} + +static bool trans_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a) +{ + return do_fmpyadd_d(ctx, a, false); +} + +static bool trans_fmpysub_d(DisasContext *ctx, arg_mpyadd *a) +{ + return do_fmpyadd_d(ctx, a, true); } -static DisasJumpType trans_fmpyfadd_d(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fmpyfadd_f(DisasContext *ctx, arg_fmpyfadd_f *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned neg = extract32(insn, 5, 1); - unsigned rm1 = extract32(insn, 21, 5); - unsigned rm2 = extract32(insn, 16, 5); - unsigned ra3 = assemble_rc64(insn); - TCGv_i64 a, b, c; + TCGv_i32 x, y, z; nullify_over(ctx); - a = load_frd0(rm1); - b = load_frd0(rm2); - c = load_frd0(ra3); + x = load_frw0_i32(a->rm1); + y = load_frw0_i32(a->rm2); + z = load_frw0_i32(a->ra3); - if (neg) { - gen_helper_fmpynfadd_d(a, cpu_env, a, b, c); + if (a->neg) { + gen_helper_fmpynfadd_s(x, cpu_env, x, y, z); } else { - gen_helper_fmpyfadd_d(a, cpu_env, a, b, c); + gen_helper_fmpyfadd_s(x, cpu_env, x, y, z); } - tcg_temp_free_i64(b); - tcg_temp_free_i64(c); - save_frd(rt, a); - tcg_temp_free_i64(a); - return nullify_end(ctx, DISAS_NEXT); + tcg_temp_free_i32(y); + tcg_temp_free_i32(z); + save_frw_i32(a->t, x); + tcg_temp_free_i32(x); + return nullify_end(ctx); } -static const DisasInsn table_fp_fused[] = { - { 0xb8000000u, 0xfc000800u, trans_fmpyfadd_s }, - { 0xb8000800u, 0xfc0019c0u, trans_fmpyfadd_d } -}; - -static DisasJumpType translate_table_int(DisasContext *ctx, uint32_t insn, - const DisasInsn table[], size_t n) +static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a) { - size_t i; - for (i = 0; i < n; ++i) { - if ((insn & table[i].mask) == table[i].insn) { - return table[i].trans(ctx, insn, &table[i]); - } - } - qemu_log_mask(LOG_UNIMP, "UNIMP insn %08x @ " TARGET_FMT_lx "\n", - insn, ctx->base.pc_next); - return gen_illegal(ctx); -} - -#define translate_table(ctx, insn, table) \ - translate_table_int(ctx, insn, table, ARRAY_SIZE(table)) - -static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) -{ - uint32_t opc = extract32(insn, 26, 6); - - switch (opc) { - case 0x00: /* system op */ - return translate_table(ctx, insn, table_system); - case 0x01: - return translate_table(ctx, insn, table_mem_mgmt); - case 0x02: - return translate_table(ctx, insn, table_arith_log); - case 0x03: - return translate_table(ctx, insn, table_index_mem); - case 0x06: - return trans_fmpyadd(ctx, insn, false); - case 0x08: - return trans_ldil(ctx, insn); - case 0x09: - return trans_copr_w(ctx, insn); - case 0x0A: - return trans_addil(ctx, insn); - case 0x0B: - return trans_copr_dw(ctx, insn); - case 0x0C: - return translate_table(ctx, insn, table_float_0c); - case 0x0D: - return trans_ldo(ctx, insn); - case 0x0E: - return translate_table(ctx, insn, table_float_0e); - - case 0x10: - return trans_load(ctx, insn, false, MO_UB); - case 0x11: - return trans_load(ctx, insn, false, MO_TEUW); - case 0x12: - return trans_load(ctx, insn, false, MO_TEUL); - case 0x13: - return trans_load(ctx, insn, true, MO_TEUL); - case 0x16: - return trans_fload_mod(ctx, insn); - case 0x17: - return trans_load_w(ctx, insn); - case 0x18: - return trans_store(ctx, insn, false, MO_UB); - case 0x19: - return trans_store(ctx, insn, false, MO_TEUW); - case 0x1A: - return trans_store(ctx, insn, false, MO_TEUL); - case 0x1B: - return trans_store(ctx, insn, true, MO_TEUL); - case 0x1E: - return trans_fstore_mod(ctx, insn); - case 0x1F: - return trans_store_w(ctx, insn); - - case 0x20: - return trans_cmpb(ctx, insn, true, false, false); - case 0x21: - return trans_cmpb(ctx, insn, true, true, false); - case 0x22: - return trans_cmpb(ctx, insn, false, false, false); - case 0x23: - return trans_cmpb(ctx, insn, false, true, false); - case 0x24: - return trans_cmpiclr(ctx, insn); - case 0x25: - return trans_subi(ctx, insn); - case 0x26: - return trans_fmpyadd(ctx, insn, true); - case 0x27: - return trans_cmpb(ctx, insn, true, false, true); - case 0x28: - return trans_addb(ctx, insn, true, false); - case 0x29: - return trans_addb(ctx, insn, true, true); - case 0x2A: - return trans_addb(ctx, insn, false, false); - case 0x2B: - return trans_addb(ctx, insn, false, true); - case 0x2C: - case 0x2D: - return trans_addi(ctx, insn); - case 0x2E: - return translate_table(ctx, insn, table_fp_fused); - case 0x2F: - return trans_cmpb(ctx, insn, false, false, true); - - case 0x30: - case 0x31: - return trans_bb(ctx, insn); - case 0x32: - return trans_movb(ctx, insn, false); - case 0x33: - return trans_movb(ctx, insn, true); - case 0x34: - return translate_table(ctx, insn, table_sh_ex); - case 0x35: - return translate_table(ctx, insn, table_depw); - case 0x38: - return trans_be(ctx, insn, false); - case 0x39: - return trans_be(ctx, insn, true); - case 0x3A: - return translate_table(ctx, insn, table_branch); - - case 0x04: /* spopn */ - case 0x05: /* diag */ - case 0x0F: /* product specific */ - break; + TCGv_i64 x, y, z; - case 0x07: /* unassigned */ - case 0x15: /* unassigned */ - case 0x1D: /* unassigned */ - case 0x37: /* unassigned */ - break; - case 0x3F: -#ifndef CONFIG_USER_ONLY - /* Unassigned, but use as system-halt. */ - if (insn == 0xfffdead0) { - return gen_hlt(ctx, 0); /* halt system */ - } - if (insn == 0xfffdead1) { - return gen_hlt(ctx, 1); /* reset system */ - } -#endif - break; - default: - break; + nullify_over(ctx); + x = load_frd0(a->rm1); + y = load_frd0(a->rm2); + z = load_frd0(a->ra3); + + if (a->neg) { + gen_helper_fmpynfadd_d(x, cpu_env, x, y, z); + } else { + gen_helper_fmpyfadd_d(x, cpu_env, x, y, z); } - return gen_illegal(ctx); + + tcg_temp_free_i64(y); + tcg_temp_free_i64(z); + save_frd(a->t, x); + tcg_temp_free_i64(x); + return nullify_end(ctx); } static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) @@ -4733,7 +4109,7 @@ static bool hppa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, { DisasContext *ctx = container_of(dcbase, DisasContext, base); - ctx->base.is_jmp = gen_excp(ctx, EXCP_DEBUG); + gen_excp(ctx, EXCP_DEBUG); ctx->base.pc_next += 4; return true; } @@ -4748,7 +4124,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Execute one insn. */ #ifdef CONFIG_USER_ONLY if (ctx->base.pc_next < TARGET_PAGE_SIZE) { - ret = do_page_zero(ctx); + do_page_zero(ctx); + ret = ctx->base.is_jmp; assert(ret != DISAS_NEXT); } else #endif @@ -4773,7 +4150,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ret = DISAS_NEXT; } else { ctx->insn = insn; - ret = translate_one(ctx, insn); + if (!decode(ctx, insn)) { + gen_illegal(ctx); + } + ret = ctx->base.is_jmp; assert(ctx->null_lab == NULL); } } @@ -4799,14 +4179,13 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); - ret = DISAS_NORETURN; + ctx->base.is_jmp = ret = DISAS_NORETURN; } else { - ret = DISAS_IAQ_N_STALE; + ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE; } } ctx->iaoq_f = ctx->iaoq_b; ctx->iaoq_b = ctx->iaoq_n; - ctx->base.is_jmp = ret; ctx->base.pc_next += 4; if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) { diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 28d7e5302f..cc3ddc0ae4 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -88,7 +88,7 @@ typedef struct RISCVCPUInfo { static void set_misa(CPURISCVState *env, target_ulong misa) { - env->misa = misa; + env->misa_mask = env->misa = misa; } static void set_versions(CPURISCVState *env, int user_ver, int priv_ver) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 743f02c8b9..5c2aebf132 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -86,7 +86,8 @@ so a cpu features bitfield is required, likewise for optional PMP support */ enum { RISCV_FEATURE_MMU, - RISCV_FEATURE_PMP + RISCV_FEATURE_PMP, + RISCV_FEATURE_MISA }; #define USER_VERSION_2_02_0 0x00020200 @@ -118,6 +119,7 @@ struct CPURISCVState { target_ulong user_ver; target_ulong priv_ver; target_ulong misa; + target_ulong misa_mask; uint32_t features; @@ -256,7 +258,7 @@ int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); -#define cpu_signal_handler cpu_riscv_signal_handler +#define cpu_signal_handler riscv_cpu_signal_handler #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index @@ -264,19 +266,18 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ #endif -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv); +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); void riscv_translate_init(void); -RISCVCPU *cpu_riscv_init(const char *cpu_model); -int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc); -void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, - uint32_t exception, uintptr_t pc); +int riscv_cpu_signal_handler(int host_signum, void *pinfo, void *puc); +void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, + uint32_t exception, uintptr_t pc); -target_ulong cpu_riscv_get_fflags(CPURISCVState *env); -void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong); +target_ulong riscv_cpu_get_fflags(CPURISCVState *env); +void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); -#define TB_FLAGS_MMU_MASK 3 -#define TB_FLAGS_FP_ENABLE MSTATUS_FS +#define TB_FLAGS_MMU_MASK 3 +#define TB_FLAGS_MSTATUS_FS MSTATUS_FS static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) @@ -284,7 +285,7 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, *pc = env->pc; *cs_base = 0; #ifdef CONFIG_USER_ONLY - *flags = TB_FLAGS_FP_ENABLE; + *flags = TB_FLAGS_MSTATUS_FS; #else *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS); #endif @@ -293,13 +294,13 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); -static inline void csr_write_helper(CPURISCVState *env, target_ulong val, - int csrno) +static inline void riscv_csr_write(CPURISCVState *env, int csrno, + target_ulong val) { riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS)); } -static inline target_ulong csr_read_helper(CPURISCVState *env, int csrno) +static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) { target_ulong val = 0; riscv_csrrw(env, csrno, &val, 0, 0); diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 5439f4719e..7afcb2468d 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -311,10 +311,21 @@ #define MSTATUS32_SD 0x80000000 #define MSTATUS64_SD 0x8000000000000000ULL +#define MISA32_MXL 0xC0000000 +#define MISA64_MXL 0xC000000000000000ULL + +#define MXL_RV32 1 +#define MXL_RV64 2 +#define MXL_RV128 3 + #if defined(TARGET_RISCV32) #define MSTATUS_SD MSTATUS32_SD +#define MISA_MXL MISA32_MXL +#define MXL_VAL MXL_RV32 #elif defined(TARGET_RISCV64) #define MSTATUS_SD MSTATUS64_SD +#define MISA_MXL MISA64_MXL +#define MXL_VAL MXL_RV64 #endif /* sstatus CSR bits */ diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f257050f12..f49e98ed59 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -93,7 +93,7 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) return old; } -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) { if (newpriv > PRV_M) { g_assert_not_reached(); @@ -366,7 +366,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, g_assert_not_reached(); } env->badaddr = addr; - do_raise_exception_err(env, cs->exception_index, retaddr); + riscv_raise_exception(env, cs->exception_index, retaddr); } /* called by qemu's softmmu to fill the qemu tlb */ @@ -378,7 +378,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, if (ret == TRANSLATE_FAIL) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - do_raise_exception_err(env, cs->exception_index, retaddr); + riscv_raise_exception(env, cs->exception_index, retaddr); } } @@ -530,7 +530,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); env->mstatus = s; - riscv_set_mode(env, PRV_S); + riscv_cpu_set_mode(env, PRV_S); } else { /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ env->pc = env->mtvec; @@ -555,7 +555,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); env->mstatus = s; - riscv_set_mode(env, PRV_M); + riscv_cpu_set_mode(env, PRV_M); } /* TODO yield load reservation */ #endif diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 5e7e7d16b8..960d2b0aa9 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -56,9 +56,15 @@ static int fs(CPURISCVState *env, int csrno) static int ctr(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) - target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : - env->priv == PRV_S ? env->mcounteren : -1U; - if (!(ctr_en & (1 << (csrno & 31)))) { + uint32_t ctr_en = ~0u; + + if (env->priv < PRV_M) { + ctr_en &= env->mcounteren; + } + if (env->priv < PRV_S) { + ctr_en &= env->scounteren; + } + if (!(ctr_en & (1u << (csrno & 31)))) { return -1; } #endif @@ -90,7 +96,7 @@ static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val) return -1; } #endif - *val = cpu_riscv_get_fflags(env); + *val = riscv_cpu_get_fflags(env); return 0; } @@ -102,7 +108,7 @@ static int write_fflags(CPURISCVState *env, int csrno, target_ulong val) } env->mstatus |= MSTATUS_FS; #endif - cpu_riscv_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); + riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); return 0; } @@ -136,7 +142,7 @@ static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) return -1; } #endif - *val = (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT) + *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT) | (env->frm << FSR_RD_SHIFT); return 0; } @@ -150,7 +156,7 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) env->mstatus |= MSTATUS_FS; #endif env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; - cpu_riscv_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); + riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); return 0; } @@ -305,7 +311,8 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) } mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | - MSTATUS_MPP | MSTATUS_MXR; + MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | + MSTATUS_TW; } /* silenty discard mstatus.mpp writes for unsupported modes */ @@ -317,18 +324,6 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) mstatus = (mstatus & ~mask) | (val & mask); - /* Note: this is a workaround for an issue where mstatus.FS - does not report dirty after floating point operations - that modify floating point state. This workaround is - technically compliant with the RISC-V Privileged - specification as it is legal to return only off, or dirty. - at the expense of extra floating point save/restore. */ - - /* FP is always dirty or off */ - if (mstatus & MSTATUS_FS) { - mstatus |= MSTATUS_FS; - } - int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | ((mstatus & MSTATUS_XS) == MSTATUS_XS); mstatus = set_field(mstatus, MSTATUS_SD, dirty); @@ -343,6 +338,58 @@ static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) return 0; } +static int write_misa(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MISA)) { + /* drop write to misa */ + return 0; + } + + /* 'I' or 'E' must be present */ + if (!(val & (RVI | RVE))) { + /* It is not, drop write to misa */ + return 0; + } + + /* '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 0; + } + + /* Mask extensions that are not supported by this hart */ + val &= env->misa_mask; + + /* Mask extensions that are not supported by QEMU */ + val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + + /* '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 + */ + if ((val & RVC) && (GETPC() & ~3) != 0) { + val &= ~RVC; + } + + /* misa.MXL writes are not supported by QEMU */ + val = (env->misa & MISA_MXL) | (val & ~MISA_MXL); + + /* flush translation cache */ + if (val != env->misa) { + tb_flush(CPU(riscv_env_get_cpu(env))); + } + + env->misa = val; + + return 0; +} + static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val) { *val = env->medeleg; @@ -654,7 +701,11 @@ static int read_satp(CPURISCVState *env, int csrno, target_ulong *val) if (!riscv_feature(env, RISCV_FEATURE_MMU)) { *val = 0; } else if (env->priv_ver >= PRIV_VERSION_1_10_0) { - *val = env->satp; + if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { + return -1; + } else { + *val = env->satp; + } } else { *val = env->sptbr; } @@ -675,8 +726,12 @@ static int write_satp(CPURISCVState *env, int csrno, target_ulong val) validate_vm(env, get_field(val, SATP_MODE)) && ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) { - tlb_flush(CPU(riscv_env_get_cpu(env))); - env->satp = val; + if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { + return -1; + } else { + tlb_flush(CPU(riscv_env_get_cpu(env))); + env->satp = val; + } } return 0; } @@ -813,7 +868,7 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Machine Trap Setup */ [CSR_MSTATUS] = { any, read_mstatus, write_mstatus }, - [CSR_MISA] = { any, read_misa }, + [CSR_MISA] = { any, read_misa, write_misa }, [CSR_MIDELEG] = { any, read_mideleg, write_mideleg }, [CSR_MEDELEG] = { any, read_medeleg, write_medeleg }, [CSR_MIE] = { any, read_mie, write_mie }, diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 01b45ca0ae..b4f818a646 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -22,7 +22,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" -target_ulong cpu_riscv_get_fflags(CPURISCVState *env) +target_ulong riscv_cpu_get_fflags(CPURISCVState *env) { int soft = get_float_exception_flags(&env->fp_status); target_ulong hard = 0; @@ -36,7 +36,7 @@ target_ulong cpu_riscv_get_fflags(CPURISCVState *env) return hard; } -void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard) +void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard) { int soft = 0; @@ -73,7 +73,7 @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) softrm = float_round_ties_away; break; default: - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } set_float_rounding_mode(softrm, &env->fp_status); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 81bd1a77ea..b7dc18a41e 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -25,7 +25,7 @@ #include "exec/helper-proto.h" /* Exceptions processing helpers */ -void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, +void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, uint32_t exception, uintptr_t pc) { CPUState *cs = CPU(riscv_env_get_cpu(env)); @@ -36,7 +36,7 @@ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, void helper_raise_exception(CPURISCVState *env, uint32_t exception) { - do_raise_exception_err(env, exception, 0); + riscv_raise_exception(env, exception, 0); } target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, @@ -44,7 +44,7 @@ target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, { target_ulong val = 0; if (riscv_csrrw(env, csr, &val, src, -1) < 0) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } return val; } @@ -54,7 +54,7 @@ target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, { target_ulong val = 0; if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0) < 0) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } return val; } @@ -64,7 +64,7 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, { target_ulong val = 0; if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0) < 0) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } return val; } @@ -74,12 +74,17 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_S)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->sepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { - do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + } + + if (env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TSR)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong mstatus = env->mstatus; @@ -90,7 +95,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_SPIE)); mstatus = set_field(mstatus, MSTATUS_SPIE, 0); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); - riscv_set_mode(env, prev_priv); + riscv_cpu_set_mode(env, prev_priv); env->mstatus = mstatus; return retpc; @@ -99,12 +104,12 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_M)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->mepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { - do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } target_ulong mstatus = env->mstatus; @@ -115,7 +120,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 0); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); - riscv_set_mode(env, prev_priv); + riscv_cpu_set_mode(env, prev_priv); env->mstatus = mstatus; return retpc; @@ -125,16 +130,28 @@ void helper_wfi(CPURISCVState *env) { CPUState *cs = CPU(riscv_env_get_cpu(env)); - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); + if (env->priv == PRV_S && + env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TW)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else { + cs->halted = 1; + cs->exception_index = EXCP_HLT; + cpu_loop_exit(cs); + } } void helper_tlb_flush(CPURISCVState *env) { RISCVCPU *cpu = riscv_env_get_cpu(env); CPUState *cs = CPU(cpu); - tlb_flush(cs); + if (env->priv == PRV_S && + env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TVM)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else { + tlb_flush(cs); + } } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 312bf298b3..b7176cbf98 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -43,8 +43,10 @@ typedef struct DisasContext { DisasContextBase base; /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; + target_ulong priv_ver; uint32_t opcode; - uint32_t flags; + uint32_t mstatus_fs; + uint32_t misa; uint32_t mem_idx; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for @@ -74,6 +76,11 @@ static const int tcg_memop_lookup[8] = { #define CASE_OP_32_64(X) case X #endif +static inline bool has_ext(DisasContext *ctx, uint32_t ext) +{ + return ctx->misa & ext; +} + static void generate_exception(DisasContext *ctx, int excp) { tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); @@ -284,24 +291,42 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_gen_and_tl(source1, source1, source2); break; CASE_OP_32_64(OPC_RISC_MUL): + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_mul_tl(source1, source1, source2); break; case OPC_RISC_MULH: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_muls2_tl(source2, source1, source1, source2); break; case OPC_RISC_MULHSU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } gen_mulhsu(source1, source1, source2); break; case OPC_RISC_MULHU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_mulu2_tl(source2, source1, source1, source2); break; #if defined(TARGET_RISCV64) case OPC_RISC_DIVW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32s_tl(source1, source1); tcg_gen_ext32s_tl(source2, source2); /* fall through to DIV */ #endif case OPC_RISC_DIV: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } /* Handle by altering args to tcg_gen_div to produce req'd results: * For overflow: want source1 in source1 and 1 in source2 * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */ @@ -333,11 +358,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, break; #if defined(TARGET_RISCV64) case OPC_RISC_DIVUW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32u_tl(source1, source1); tcg_gen_ext32u_tl(source2, source2); /* fall through to DIVU */ #endif case OPC_RISC_DIVU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } cond1 = tcg_temp_new(); zeroreg = tcg_const_tl(0); resultopt1 = tcg_temp_new(); @@ -357,11 +388,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, break; #if defined(TARGET_RISCV64) case OPC_RISC_REMW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32s_tl(source1, source1); tcg_gen_ext32s_tl(source2, source2); /* fall through to REM */ #endif case OPC_RISC_REM: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } cond1 = tcg_temp_new(); cond2 = tcg_temp_new(); zeroreg = tcg_const_tl(0); @@ -389,11 +426,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, break; #if defined(TARGET_RISCV64) case OPC_RISC_REMUW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32u_tl(source1, source1); tcg_gen_ext32u_tl(source2, source2); /* fall through to REMU */ #endif case OPC_RISC_REMU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } cond1 = tcg_temp_new(); zeroreg = tcg_const_tl(0); resultopt1 = tcg_temp_new(); @@ -411,6 +454,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_temp_free(zeroreg); tcg_temp_free(resultopt1); break; + do_illegal: default: gen_exception_illegal(ctx); return; @@ -505,14 +549,13 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd, tcg_temp_free(source1); } -static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, - target_ulong imm) +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 (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { if ((next_pc & 0x3) != 0) { gen_exception_inst_addr_mis(ctx); return; @@ -526,8 +569,8 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, target_long imm) +static void gen_jalr(DisasContext *ctx, uint32_t opc, int rd, int rs1, + target_long imm) { /* no chaining with JALR */ TCGLabel *misaligned = NULL; @@ -539,7 +582,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, tcg_gen_addi_tl(cpu_pc, cpu_pc, imm); tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { misaligned = gen_new_label(); tcg_gen_andi_tl(t0, cpu_pc, 0x2); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); @@ -564,8 +607,8 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, tcg_temp_free(t0); } -static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rs1, int rs2, target_long bimm) +static void gen_branch(DisasContext *ctx, uint32_t opc, int rs1, int rs2, + target_long bimm) { TCGLabel *l = gen_new_label(); TCGv source1, source2; @@ -602,7 +645,7 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_goto_tb(ctx, 1, ctx->pc_succ_insn); gen_set_label(l); /* branch taken */ - if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { + if (!has_ext(ctx, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { /* misaligned */ gen_exception_inst_addr_mis(ctx); } else { @@ -651,12 +694,37 @@ static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2, tcg_temp_free(dat); } +#ifndef CONFIG_USER_ONLY +/* The states of mstatus_fs are: + * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty + * We will have already diagnosed disabled state, + * and need to turn initial/clean into dirty. + */ +static void mark_fs_dirty(DisasContext *ctx) +{ + TCGv tmp; + if (ctx->mstatus_fs == MSTATUS_FS) { + return; + } + /* Remember the state change for the rest of the TB. */ + ctx->mstatus_fs = MSTATUS_FS; + + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_temp_free(tmp); +} +#else +static inline void mark_fs_dirty(DisasContext *ctx) { } +#endif + static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, target_long imm) { TCGv t0; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { gen_exception_illegal(ctx); return; } @@ -667,18 +735,27 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, switch (opc) { case OPC_RISC_FLW: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL); /* RISC-V requires NaN-boxing of narrower width floating point values */ tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL); break; case OPC_RISC_FLD: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ); break; + do_illegal: default: gen_exception_illegal(ctx); break; } tcg_temp_free(t0); + + mark_fs_dirty(ctx); } static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, @@ -686,7 +763,7 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, { TCGv t0; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { gen_exception_illegal(ctx); return; } @@ -697,11 +774,18 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, switch (opc) { case OPC_RISC_FSW: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL); break; case OPC_RISC_FSD: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -865,15 +949,22 @@ static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FMADD_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FMADD_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -885,15 +976,22 @@ static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FMSUB_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FMSUB_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -905,15 +1003,22 @@ static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FNMSUB_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FNMSUB_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -925,15 +1030,22 @@ static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FNMADD_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FNMADD_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -944,37 +1056,59 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, int rs2, int rm) { TCGv t0 = NULL; + bool fp_output = true; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { goto do_illegal; } switch (opc) { case OPC_RISC_FADD_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSUB_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FMUL_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FDIV_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSQRT_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); break; case OPC_RISC_FSGNJ_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN); break; case OPC_RISC_FMIN_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } /* also handles: OPC_RISC_FMAX_S */ switch (rm) { case 0x0: @@ -990,6 +1124,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FEQ_S: /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rm) { case 0x0: @@ -1006,10 +1143,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_W_S: /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rs2) { case 0: /* FCVT_W_S */ @@ -1035,10 +1176,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_S_W: /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); switch (rs2) { @@ -1068,6 +1213,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FMV_X_S: /* also OPC_RISC_FCLASS_S */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rm) { case 0: /* FMV */ @@ -1085,9 +1233,13 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FMV_S_X: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); #if defined(TARGET_RISCV64) @@ -1100,22 +1252,37 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, /* double */ case OPC_RISC_FADD_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSUB_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FMUL_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FDIV_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSQRT_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); break; @@ -1125,6 +1292,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FMIN_D: /* also OPC_RISC_FMAX_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rm) { case 0: gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); @@ -1138,6 +1308,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, break; case OPC_RISC_FCVT_S_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rs2) { case 1: gen_set_rm(ctx, rm); @@ -1149,6 +1322,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, break; case OPC_RISC_FCVT_D_S: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rs2) { case 0: gen_set_rm(ctx, rm); @@ -1161,6 +1337,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FEQ_D: /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rm) { case 0: @@ -1177,10 +1356,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_W_D: /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rs2) { case 0: @@ -1206,10 +1389,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_D_W: /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); switch (rs2) { @@ -1239,6 +1426,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FMV_X_D: /* also OPC_RISC_FCLASS_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rm) { #if defined(TARGET_RISCV64) case 0: /* FMV */ @@ -1254,10 +1444,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, default: goto do_illegal; } + fp_output = false; break; #if defined(TARGET_RISCV64) case OPC_RISC_FMV_D_X: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); tcg_gen_mov_tl(cpu_fpr[rd], t0); @@ -1271,12 +1465,16 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, tcg_temp_free(t0); } gen_exception_illegal(ctx); - break; + return; + } + + if (fp_output) { + mark_fs_dirty(ctx); } } -static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, int csr) +static void gen_system(DisasContext *ctx, uint32_t opc, int rd, int rs1, + int csr) { TCGv source1, csr_store, dest, rs1_pass, imm_rs1; source1 = tcg_temp_new(); @@ -1292,7 +1490,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, #ifndef CONFIG_USER_ONLY /* Extract funct7 value and check whether it matches SFENCE.VMA */ if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) { - if (env->priv_ver == PRIV_VERSION_1_10_0) { + if (ctx->priv_ver == PRIV_VERSION_1_10_0) { /* sfence.vma */ /* TODO: handle ASID specific fences */ gen_helper_tlb_flush(cpu_env); @@ -1322,7 +1520,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_exception_illegal(ctx); break; case 0x102: /* SRET */ - if (riscv_has_ext(env, RVS)) { + if (has_ext(ctx, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; @@ -1346,7 +1544,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_helper_wfi(cpu_env); break; case 0x104: /* SFENCE.VM */ - if (env->priv_ver <= PRIV_VERSION_1_09_1) { + if (ctx->priv_ver <= PRIV_VERSION_1_09_1) { gen_helper_tlb_flush(cpu_env); } else { gen_exception_illegal(ctx); @@ -1467,7 +1665,7 @@ static void decode_RV32_64C0(DisasContext *ctx) } } -static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C1(DisasContext *ctx) { uint8_t funct3 = extract32(ctx->opcode, 13, 3); uint8_t rd_rs1 = GET_C_RS1(ctx->opcode); @@ -1487,7 +1685,7 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) GET_C_IMM(ctx->opcode)); #else /* C.JAL(RV32) -> jal x1, offset[11:1] */ - gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode)); + gen_jal(ctx, 1, GET_C_J_IMM(ctx->opcode)); #endif break; case 2: @@ -1566,22 +1764,22 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) break; case 5: /* C.J -> jal x0, offset[11:1]*/ - gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode)); + gen_jal(ctx, 0, GET_C_J_IMM(ctx->opcode)); break; case 6: /* C.BEQZ -> beq rs1', x0, offset[8:1]*/ rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); + gen_branch(ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); break; case 7: /* C.BNEZ -> bne rs1', x0, offset[8:1]*/ rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); + gen_branch(ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); break; } } -static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C2(DisasContext *ctx) { uint8_t rd, rs2; uint8_t funct3 = extract32(ctx->opcode, 13, 3); @@ -1615,7 +1813,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) if (extract32(ctx->opcode, 12, 1) == 0) { if (rs2 == 0) { /* C.JR -> jalr x0, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0); + gen_jalr(ctx, OPC_RISC_JALR, 0, rd, 0); } else { /* C.MV -> add rd, x0, rs2 */ gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2); @@ -1623,11 +1821,11 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) } else { if (rd == 0) { /* C.EBREAK -> ebreak*/ - gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1); + gen_system(ctx, OPC_RISC_ECALL, 0, 0, 0x1); } else { if (rs2 == 0) { /* C.JALR -> jalr x1, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0); + gen_jalr(ctx, OPC_RISC_JALR, 1, rd, 0); } else { /* C.ADD -> add rd, rd, rs2 */ gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2); @@ -1659,7 +1857,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) } } -static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C(DisasContext *ctx) { uint8_t op = extract32(ctx->opcode, 0, 2); @@ -1668,15 +1866,15 @@ static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) decode_RV32_64C0(ctx); break; case 1: - decode_RV32_64C1(env, ctx); + decode_RV32_64C1(ctx); break; case 2: - decode_RV32_64C2(env, ctx); + decode_RV32_64C2(ctx); break; } } -static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64G(DisasContext *ctx) { int rs1; int rs2; @@ -1711,13 +1909,13 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) break; case OPC_RISC_JAL: imm = GET_JAL_IMM(ctx->opcode); - gen_jal(env, ctx, rd, imm); + gen_jal(ctx, rd, imm); break; case OPC_RISC_JALR: - gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); + gen_jalr(ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); break; case OPC_RISC_BRANCH: - gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, + gen_branch(ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, GET_B_IMM(ctx->opcode)); break; case OPC_RISC_LOAD: @@ -1753,6 +1951,9 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) GET_STORE_IMM(ctx->opcode)); break; case OPC_RISC_ATOMIC: + if (!has_ext(ctx, RVA)) { + goto do_illegal; + } gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2); break; case OPC_RISC_FMADD: @@ -1788,38 +1989,42 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) } break; case OPC_RISC_SYSTEM: - gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, + gen_system(ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, (ctx->opcode & 0xFFF00000) >> 20); break; + do_illegal: default: gen_exception_illegal(ctx); break; } } -static void decode_opc(CPURISCVState *env, DisasContext *ctx) +static void decode_opc(DisasContext *ctx) { /* check for compressed insn */ if (extract32(ctx->opcode, 0, 2) != 3) { - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { gen_exception_illegal(ctx); } else { ctx->pc_succ_insn = ctx->base.pc_next + 2; - decode_RV32_64C(env, ctx); + decode_RV32_64C(ctx); } } else { ctx->pc_succ_insn = ctx->base.pc_next + 4; - decode_RV32_64G(env, ctx); + decode_RV32_64G(ctx); } } static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPURISCVState *env = cs->env_ptr; ctx->pc_succ_insn = ctx->base.pc_first; - ctx->flags = ctx->base.tb->flags; ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; + ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; + ctx->priv_ver = env->priv_ver; + ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ } @@ -1850,14 +2055,13 @@ static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, return true; } - static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu->env_ptr; ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); - decode_opc(env, ctx); + decode_opc(ctx); ctx->base.pc_next = ctx->pc_succ_insn; if (ctx->base.is_jmp == DISAS_NEXT) { diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 4911b69317..942ddc9192 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -295,7 +295,7 @@ static void setup_vm_with_server(IVState *s, int nvectors) { char *cmd; - cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait " + cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s " "-device ivshmem-doorbell,chardev=chr0,vectors=%d", tmpserver, nvectors); diff --git a/tests/libqtest.c b/tests/libqtest.c index 6fb30855fa..c49b85482d 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -232,9 +232,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) qtest_add_abrt_handler(kill_qemu_hook_func, s); command = g_strdup_printf("exec %s " - "-qtest unix:%s,nowait " + "-qtest unix:%s " "-qtest-log %s " - "-chardev socket,path=%s,nowait,id=char0 " + "-chardev socket,path=%s,id=char0 " "-mon chardev=char0,mode=control " "-machine accel=qtest " "-display none " diff --git a/tests/test-char.c b/tests/test-char.c index 19c3efad72..63b4d3289d 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -11,11 +11,17 @@ #include "qapi/qapi-commands-char.h" #include "qapi/qmp/qdict.h" #include "qom/qom-qobject.h" +#include "io/channel-socket.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-sockets.h" static bool quit; typedef struct FeHandler { int read_count; + bool is_open; + int openclose_count; + bool openclose_mismatch; int last_event; char read_buf[128]; } FeHandler; @@ -49,10 +55,24 @@ static void fe_read(void *opaque, const uint8_t *buf, int size) static void fe_event(void *opaque, int event) { FeHandler *h = opaque; + bool new_open_state; h->last_event = event; - if (event != CHR_EVENT_BREAK) { + switch (event) { + case CHR_EVENT_BREAK: + break; + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + h->openclose_count++; + new_open_state = (event == CHR_EVENT_OPENED); + if (h->is_open == new_open_state) { + h->openclose_mismatch = true; + } + h->is_open = new_open_state; + /* no break */ + default: quit = true; + break; } } @@ -66,7 +86,7 @@ static void char_console_test_subprocess(void) 1, &error_abort); qemu_opt_set(opts, "backend", "console", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); qemu_chr_write_all(chr, (const uint8_t *)"CONSOLE", 7); @@ -88,7 +108,7 @@ static void char_stdio_test_subprocess(void) CharBackend be; int ret; - chr = qemu_chr_new("label", "stdio"); + chr = qemu_chr_new("label", "stdio", NULL); g_assert_nonnull(chr); qemu_chr_fe_init(&be, chr, &error_abort); @@ -119,7 +139,7 @@ static void char_ringbuf_test(void) qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "5", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_null(chr); qemu_opts_del(opts); @@ -127,7 +147,7 @@ static void char_ringbuf_test(void) 1, &error_abort); qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "2", &error_abort); - chr = qemu_chr_new_from_opts(opts, &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); g_assert_nonnull(chr); qemu_opts_del(opts); @@ -150,7 +170,7 @@ static void char_ringbuf_test(void) 1, &error_abort); qemu_opt_set(opts, "backend", "memory", &error_abort); qemu_opt_set(opts, "size", "2", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); object_unparent(OBJECT(chr)); qemu_opts_del(opts); @@ -161,7 +181,7 @@ static void char_mux_test(void) QemuOpts *opts; Chardev *chr, *base; char *data; - FeHandler h1 = { 0, }, h2 = { 0, }; + FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, }; CharBackend chr_be1, chr_be2; opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", @@ -169,7 +189,7 @@ static void char_mux_test(void) qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "128", &error_abort); qemu_opt_set(opts, "mux", "on", &error_abort); - chr = qemu_chr_new_from_opts(opts, &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); g_assert_nonnull(chr); qemu_opts_del(opts); @@ -233,6 +253,65 @@ static void char_mux_test(void) g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + /* open/close state and corresponding events */ + g_assert_true(qemu_chr_fe_backend_open(&chr_be1)); + g_assert_true(qemu_chr_fe_backend_open(&chr_be2)); + g_assert_true(h1.is_open); + g_assert_false(h1.openclose_mismatch); + g_assert_true(h2.is_open); + g_assert_false(h2.openclose_mismatch); + + h1.openclose_count = h2.openclose_count = 0; + + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, + NULL, NULL, false); + qemu_chr_fe_set_handlers(&chr_be2, NULL, NULL, NULL, NULL, + NULL, NULL, false); + g_assert_cmpint(h1.openclose_count, ==, 0); + g_assert_cmpint(h2.openclose_count, ==, 0); + + h1.is_open = h2.is_open = false; + qemu_chr_fe_set_handlers(&chr_be1, + NULL, + NULL, + fe_event, + NULL, + &h1, + NULL, false); + qemu_chr_fe_set_handlers(&chr_be2, + NULL, + NULL, + fe_event, + NULL, + &h2, + NULL, false); + g_assert_cmpint(h1.openclose_count, ==, 1); + g_assert_false(h1.openclose_mismatch); + g_assert_cmpint(h2.openclose_count, ==, 1); + g_assert_false(h2.openclose_mismatch); + + qemu_chr_be_event(base, CHR_EVENT_CLOSED); + qemu_chr_be_event(base, CHR_EVENT_OPENED); + g_assert_cmpint(h1.openclose_count, ==, 3); + g_assert_false(h1.openclose_mismatch); + g_assert_cmpint(h2.openclose_count, ==, 3); + g_assert_false(h2.openclose_mismatch); + + qemu_chr_fe_set_handlers(&chr_be2, + fe_can_read, + fe_read, + fe_event, + NULL, + &h2, + NULL, false); + qemu_chr_fe_set_handlers(&chr_be1, + fe_can_read, + fe_read, + fe_event, + NULL, + &h1, + NULL, false); + /* remove first handler */ qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, NULL, true); @@ -257,168 +336,6 @@ static void char_mux_test(void) qemu_chr_fe_deinit(&chr_be2, true); } -typedef struct SocketIdleData { - GMainLoop *loop; - Chardev *chr; - bool conn_expected; - CharBackend *be; - CharBackend *client_be; -} SocketIdleData; - -static gboolean char_socket_test_idle(gpointer user_data) -{ - SocketIdleData *data = user_data; - - if (object_property_get_bool(OBJECT(data->chr), "connected", NULL) - == data->conn_expected) { - quit = true; - return FALSE; - } - - return TRUE; -} - -static void socket_read(void *opaque, const uint8_t *buf, int size) -{ - SocketIdleData *data = opaque; - - g_assert_cmpint(size, ==, 1); - g_assert_cmpint(*buf, ==, 'Z'); - - size = qemu_chr_fe_write(data->be, (const uint8_t *)"hello", 5); - g_assert_cmpint(size, ==, 5); -} - -static int socket_can_read(void *opaque) -{ - return 10; -} - -static void socket_read_hello(void *opaque, const uint8_t *buf, int size) -{ - g_assert_cmpint(size, ==, 5); - g_assert(strncmp((char *)buf, "hello", 5) == 0); - - quit = true; -} - -static int socket_can_read_hello(void *opaque) -{ - return 10; -} - -static void char_socket_test_common(Chardev *chr, bool reconnect) -{ - Chardev *chr_client; - QObject *addr; - QDict *qdict; - const char *port; - SocketIdleData d = { .chr = chr }; - CharBackend be; - CharBackend client_be; - char *tmp; - - d.be = &be; - d.client_be = &be; - - g_assert_nonnull(chr); - g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - - addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); - qdict = qobject_to(QDict, addr); - port = qdict_get_str(qdict, "port"); - tmp = g_strdup_printf("tcp:127.0.0.1:%s%s", port, - reconnect ? ",reconnect=1" : ""); - qobject_unref(qdict); - - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, - NULL, NULL, &d, NULL, true); - - chr_client = qemu_chr_new("client", tmp); - qemu_chr_fe_init(&client_be, chr_client, &error_abort); - qemu_chr_fe_set_handlers(&client_be, socket_can_read_hello, - socket_read_hello, - NULL, NULL, &d, NULL, true); - g_free(tmp); - - d.conn_expected = true; - guint id = g_idle_add(char_socket_test_idle, &d); - g_source_set_name_by_id(id, "test-idle"); - g_assert_cmpint(id, >, 0); - main_loop(); - - d.chr = chr_client; - id = g_idle_add(char_socket_test_idle, &d); - g_source_set_name_by_id(id, "test-idle"); - g_assert_cmpint(id, >, 0); - main_loop(); - - g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - g_assert(object_property_get_bool(OBJECT(chr_client), - "connected", &error_abort)); - - qemu_chr_write_all(chr_client, (const uint8_t *)"Z", 1); - main_loop(); - - object_unparent(OBJECT(chr_client)); - - d.chr = chr; - d.conn_expected = false; - g_idle_add(char_socket_test_idle, &d); - main_loop(); - - object_unparent(OBJECT(chr)); -} - - -static void char_socket_basic_test(void) -{ - Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); - - char_socket_test_common(chr, false); -} - - -static void char_socket_reconnect_test(void) -{ - Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); - - char_socket_test_common(chr, true); -} - - -static void char_socket_fdpass_test(void) -{ - Chardev *chr; - char *optstr; - QemuOpts *opts; - int fd; - SocketAddress *addr = g_new0(SocketAddress, 1); - - addr->type = SOCKET_ADDRESS_TYPE_INET; - addr->u.inet.host = g_strdup("127.0.0.1"); - addr->u.inet.port = g_strdup("0"); - - fd = socket_listen(addr, &error_abort); - g_assert(fd >= 0); - - qapi_free_SocketAddress(addr); - - optstr = g_strdup_printf("socket,id=cdev,fd=%d,server,nowait", fd); - - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), - optstr, true); - g_free(optstr); - g_assert_nonnull(opts); - - chr = qemu_chr_new_from_opts(opts, &error_abort); - - qemu_opts_del(opts); - - char_socket_test_common(chr, false); -} - static void websock_server_read(void *opaque, const uint8_t *buf, int size) { @@ -495,7 +412,7 @@ static void char_websock_test(void) CharBackend client_be; Chardev *chr_client; Chardev *chr = qemu_chr_new("server", - "websocket:127.0.0.1:0,server,nowait"); + "websocket:127.0.0.1:0,server,nowait", NULL); const char handshake[] = "GET / HTTP/1.1\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" @@ -519,7 +436,7 @@ static void char_websock_test(void) qemu_chr_fe_set_handlers(&be, websock_server_can_read, websock_server_read, NULL, NULL, chr, NULL, true); - chr_client = qemu_chr_new("client", tmp); + chr_client = qemu_chr_new("client", tmp, NULL); qemu_chr_fe_init(&client_be, chr_client, &error_abort); qemu_chr_fe_set_handlers(&client_be, websock_client_can_read, websock_client_read, @@ -565,7 +482,7 @@ static void char_pipe_test(void) } tmp = g_strdup_printf("pipe:%s", pipe); - chr = qemu_chr_new("pipe", tmp); + chr = qemu_chr_new("pipe", tmp, NULL); g_assert_nonnull(chr); g_free(tmp); @@ -610,6 +527,28 @@ static void char_pipe_test(void) } #endif +typedef struct SocketIdleData { + GMainLoop *loop; + Chardev *chr; + bool conn_expected; + CharBackend *be; + CharBackend *client_be; +} SocketIdleData; + + +static void socket_read_hello(void *opaque, const uint8_t *buf, int size) +{ + g_assert_cmpint(size, ==, 5); + g_assert(strncmp((char *)buf, "hello", 5) == 0); + + quit = true; +} + +static int socket_can_read_hello(void *opaque) +{ + return 10; +} + static int make_udp_socket(int *port) { struct sockaddr_in addr = { 0, }; @@ -647,7 +586,7 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) int port; sock = make_udp_socket(&port); tmp = g_strdup_printf("udp:127.0.0.1:%d", port); - chr = qemu_chr_new("client", tmp); + chr = qemu_chr_new("client", tmp, NULL); g_assert_nonnull(chr); be = g_alloca(sizeof(CharBackend)); @@ -680,6 +619,391 @@ static void char_udp_test(void) char_udp_test_internal(NULL, 0); } + +typedef struct { + int event; + bool got_pong; +} CharSocketTestData; + + +#define SOCKET_PING "Hello" +#define SOCKET_PONG "World" + + +static void +char_socket_event(void *opaque, int event) +{ + CharSocketTestData *data = opaque; + data->event = event; +} + + +static void +char_socket_read(void *opaque, const uint8_t *buf, int size) +{ + CharSocketTestData *data = opaque; + g_assert_cmpint(size, ==, sizeof(SOCKET_PONG)); + g_assert(memcmp(buf, SOCKET_PONG, size) == 0); + data->got_pong = true; +} + + +static int +char_socket_can_read(void *opaque) +{ + return sizeof(SOCKET_PONG); +} + + +static char * +char_socket_addr_to_opt_str(SocketAddress *addr, bool fd_pass, + const char *reconnect, bool is_listen) +{ + if (fd_pass) { + QIOChannelSocket *ioc = qio_channel_socket_new(); + int fd; + char *optstr; + g_assert(!reconnect); + if (is_listen) { + qio_channel_socket_listen_sync(ioc, addr, &error_abort); + } else { + qio_channel_socket_connect_sync(ioc, addr, &error_abort); + } + fd = ioc->fd; + ioc->fd = -1; + optstr = g_strdup_printf("socket,id=cdev0,fd=%d%s", + fd, is_listen ? ",server,nowait" : ""); + object_unref(OBJECT(ioc)); + return optstr; + } else { + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("socket,id=cdev0,host=%s,port=%s%s%s", + addr->u.inet.host, + addr->u.inet.port, + reconnect ? reconnect : "", + is_listen ? ",server,nowait" : ""); + + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("socket,id=cdev0,path=%s%s%s", + addr->u.q_unix.path, + reconnect ? reconnect : "", + is_listen ? ",server,nowait" : ""); + + default: + g_assert_not_reached(); + } + } +} + + +static void +char_socket_ping_pong(QIOChannel *ioc) +{ + char greeting[sizeof(SOCKET_PING)]; + const char *response = SOCKET_PONG; + + qio_channel_read_all(ioc, greeting, sizeof(greeting), &error_abort); + + g_assert(memcmp(greeting, SOCKET_PING, sizeof(greeting)) == 0); + + qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), &error_abort); + + object_unref(OBJECT(ioc)); +} + + +static gpointer +char_socket_server_client_thread(gpointer data) +{ + SocketAddress *addr = data; + QIOChannelSocket *ioc = qio_channel_socket_new(); + + qio_channel_socket_connect_sync(ioc, addr, &error_abort); + + char_socket_ping_pong(QIO_CHANNEL(ioc)); + + return NULL; +} + + +typedef struct { + SocketAddress *addr; + bool wait_connected; + bool fd_pass; +} CharSocketServerTestConfig; + + +static void char_socket_server_test(gconstpointer opaque) +{ + const CharSocketServerTestConfig *config = opaque; + Chardev *chr; + CharBackend be = {0}; + CharSocketTestData data = {0}; + QObject *qaddr; + SocketAddress *addr; + Visitor *v; + QemuThread thread; + int ret; + bool reconnected; + char *optstr; + QemuOpts *opts; + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + /* + * We rely on config->addr containing "nowait", otherwise + * qemu_chr_new() will block until a client connects. We + * can't spawn our client thread though, because until + * qemu_chr_new() returns we don't know what TCP port was + * allocated by the OS + */ + optstr = char_socket_addr_to_opt_str(config->addr, + config->fd_pass, + NULL, + true); + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + g_assert_nonnull(qaddr); + + v = qobject_input_visitor_new(qaddr); + visit_type_SocketAddress(v, "addr", &addr, &error_abort); + visit_free(v); + qobject_unref(qaddr); + + qemu_chr_fe_init(&be, chr, &error_abort); + + reconnect: + data.event = -1; + qemu_chr_fe_set_handlers(&be, NULL, NULL, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == -1); + + /* + * Kick off a thread to act as the "remote" client + * which just plays ping-pong with us + */ + qemu_thread_create(&thread, "client", + char_socket_server_client_thread, + addr, QEMU_THREAD_JOINABLE); + g_assert(data.event == -1); + + if (config->wait_connected) { + /* Synchronously accept a connection */ + qemu_chr_wait_connected(chr, &error_abort); + } else { + /* + * Asynchronously accept a connection when the evnt + * loop reports the listener socket as readable + */ + while (data.event == -1) { + main_loop_wait(false); + } + } + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Send a greeting to the client */ + ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, + sizeof(SOCKET_PING)); + g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); + g_assert(data.event == -1); + + /* Setup a callback to receive the reply to our greeting */ + qemu_chr_fe_set_handlers(&be, char_socket_can_read, + char_socket_read, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Wait for the client to go away */ + while (data.event == -1) { + main_loop_wait(false); + } + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.event == CHR_EVENT_CLOSED); + g_assert(data.got_pong); + + qemu_thread_join(&thread); + + if (!reconnected) { + reconnected = true; + goto reconnect; + } + + qapi_free_SocketAddress(addr); + object_unparent(OBJECT(chr)); + g_free(optstr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + + +static gpointer +char_socket_client_server_thread(gpointer data) +{ + QIOChannelSocket *ioc = data; + QIOChannelSocket *cioc; + + cioc = qio_channel_socket_accept(ioc, &error_abort); + g_assert_nonnull(cioc); + + char_socket_ping_pong(QIO_CHANNEL(cioc)); + + return NULL; +} + + +typedef struct { + SocketAddress *addr; + const char *reconnect; + bool wait_connected; + bool fd_pass; +} CharSocketClientTestConfig; + + +static void char_socket_client_test(gconstpointer opaque) +{ + const CharSocketClientTestConfig *config = opaque; + QIOChannelSocket *ioc; + char *optstr; + Chardev *chr; + CharBackend be = {0}; + CharSocketTestData data = {0}; + SocketAddress *addr; + QemuThread thread; + int ret; + bool reconnected = false; + QemuOpts *opts; + + /* + * Setup a listener socket and determine get its address + * so we know the TCP port for the client later + */ + ioc = qio_channel_socket_new(); + g_assert_nonnull(ioc); + qio_channel_socket_listen_sync(ioc, config->addr, &error_abort); + addr = qio_channel_socket_get_local_address(ioc, &error_abort); + g_assert_nonnull(addr); + + /* + * Kick off a thread to act as the "remote" client + * which just plays ping-pong with us + */ + qemu_thread_create(&thread, "client", + char_socket_client_server_thread, + ioc, QEMU_THREAD_JOINABLE); + + /* + * Populate the chardev address based on what the server + * is actually listening on + */ + optstr = char_socket_addr_to_opt_str(addr, + config->fd_pass, + config->reconnect, + false); + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + + if (config->reconnect) { + /* + * If reconnect is set, the connection will be + * established in a background thread and we won't + * see the "connected" status updated until we + * run the main event loop, or call qemu_chr_wait_connected + */ + g_assert(!object_property_get_bool(OBJECT(chr), "connected", + &error_abort)); + } else { + g_assert(object_property_get_bool(OBJECT(chr), "connected", + &error_abort)); + } + + qemu_chr_fe_init(&be, chr, &error_abort); + + reconnect: + data.event = -1; + qemu_chr_fe_set_handlers(&be, NULL, NULL, + char_socket_event, NULL, + &data, NULL, true); + if (config->reconnect) { + g_assert(data.event == -1); + } else { + g_assert(data.event == CHR_EVENT_OPENED); + } + + if (config->wait_connected) { + /* + * Synchronously wait for the connection to complete + * This should be a no-op if reconnect is not set. + */ + qemu_chr_wait_connected(chr, &error_abort); + } else { + /* + * Asynchronously wait for the connection to be reported + * as complete when the background thread reports its + * status. + * The loop will short-circuit if reconnect was set + */ + while (data.event == -1) { + main_loop_wait(false); + } + } + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + /* Send a greeting to the server */ + ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, + sizeof(SOCKET_PING)); + g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); + g_assert(data.event == -1); + + /* Setup a callback to receive the reply to our greeting */ + qemu_chr_fe_set_handlers(&be, char_socket_can_read, + char_socket_read, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Wait for the server to go away */ + while (data.event == -1) { + main_loop_wait(false); + } + g_assert(data.event == CHR_EVENT_CLOSED); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.got_pong); + qemu_thread_join(&thread); + + if (config->reconnect && !reconnected) { + reconnected = true; + qemu_thread_create(&thread, "client", + char_socket_client_server_thread, + ioc, QEMU_THREAD_JOINABLE); + goto reconnect; + } + + object_unref(OBJECT(ioc)); + object_unparent(OBJECT(chr)); + qapi_free_SocketAddress(addr); + g_free(optstr); +} + + #ifdef HAVE_CHARDEV_SERIAL static void char_serial_test(void) { @@ -691,14 +1015,14 @@ static void char_serial_test(void) qemu_opt_set(opts, "backend", "serial", &error_abort); qemu_opt_set(opts, "path", "/dev/null", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); /* TODO: add more tests with a pty */ object_unparent(OBJECT(chr)); /* test tty alias */ qemu_opt_set(opts, "backend", "tty", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); object_unparent(OBJECT(chr)); @@ -731,7 +1055,7 @@ static void char_file_fifo_test(void) g_assert_cmpint(ret, ==, 8); chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, - &error_abort); + NULL, &error_abort); qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, @@ -785,7 +1109,7 @@ static void char_file_test_internal(Chardev *ext_chr, const char *filepath) out = g_build_filename(tmp_path, "out", NULL); file.out = out; chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, - &error_abort); + NULL, &error_abort); } ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6); g_assert_cmpint(ret, ==, 6); @@ -820,7 +1144,7 @@ static void char_null_test(void) chr = qemu_chr_find("label-null"); g_assert_null(chr); - chr = qemu_chr_new("label-null", "null"); + chr = qemu_chr_new("label-null", "null", NULL); chr = qemu_chr_find("label-null"); g_assert_nonnull(chr); @@ -856,9 +1180,10 @@ static void char_null_test(void) static void char_invalid_test(void) { Chardev *chr; - - chr = qemu_chr_new("label-invalid", "invalid"); + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + chr = qemu_chr_new("label-invalid", "invalid", NULL); g_assert_null(chr); + g_unsetenv("QTEST_SILENT_ERRORS"); } static int chardev_change(void *opaque) @@ -890,7 +1215,7 @@ static void char_hotswap_test(void) chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); - chr = qemu_chr_new("chardev", chr_args); + chr = qemu_chr_new("chardev", chr_args, NULL); qemu_chr_fe_init(&be, chr, &error_abort); /* check that chardev operates correctly */ @@ -958,9 +1283,71 @@ int main(int argc, char **argv) #ifndef _WIN32 g_test_add_func("/char/file-fifo", char_file_fifo_test); #endif - g_test_add_func("/char/socket/basic", char_socket_basic_test); - g_test_add_func("/char/socket/reconnect", char_socket_reconnect_test); - g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test); + + SocketAddress tcpaddr = { + .type = SOCKET_ADDRESS_TYPE_INET, + .u.inet.host = (char *)"127.0.0.1", + .u.inet.port = (char *)"0", + }; +#ifndef WIN32 + SocketAddress unixaddr = { + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = (char *)"test-char.sock", + }; +#endif + +#define SOCKET_SERVER_TEST(name, addr) \ + CharSocketServerTestConfig server1 ## name = \ + { addr, false, false }; \ + CharSocketServerTestConfig server2 ## name = \ + { addr, true, false }; \ + CharSocketServerTestConfig server3 ## name = \ + { addr, false, true }; \ + CharSocketServerTestConfig server4 ## name = \ + { addr, true, true }; \ + g_test_add_data_func("/char/socket/server/mainloop/" # name, \ + &server1 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/wait-conn/" # name, \ + &server2 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/mainloop-fdpass/" # name, \ + &server3 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/wait-conn-fdpass/" # name, \ + &server4 ##name, char_socket_server_test) + +#define SOCKET_CLIENT_TEST(name, addr) \ + CharSocketClientTestConfig client1 ## name = \ + { addr, NULL, false, false }; \ + CharSocketClientTestConfig client2 ## name = \ + { addr, NULL, true, false }; \ + CharSocketClientTestConfig client3 ## name = \ + { addr, ",reconnect=1", false }; \ + CharSocketClientTestConfig client4 ## name = \ + { addr, ",reconnect=1", true }; \ + CharSocketClientTestConfig client5 ## name = \ + { addr, NULL, false, true }; \ + CharSocketClientTestConfig client6 ## name = \ + { addr, NULL, true, true }; \ + g_test_add_data_func("/char/socket/client/mainloop/" # name, \ + &client1 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn/" # name, \ + &client2 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/mainloop-reconnect/" # name, \ + &client3 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn-reconnect/" # name, \ + &client4 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/mainloop-fdpass/" # name, \ + &client5 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \ + &client6 ##name, char_socket_client_test) + + SOCKET_SERVER_TEST(tcp, &tcpaddr); + SOCKET_CLIENT_TEST(tcp, &tcpaddr); +#ifndef WIN32 + SOCKET_SERVER_TEST(unix, &unixaddr); + SOCKET_CLIENT_TEST(unix, &unixaddr); +#endif + + g_test_add_func("/char/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); diff --git a/tests/test-filter-redirector.c b/tests/test-filter-redirector.c index 9ca9feabf8..6dc21dd4fb 100644 --- a/tests/test-filter-redirector.c +++ b/tests/test-filter-redirector.c @@ -96,7 +96,7 @@ static void test_redirector_tx(void) "-device %s,netdev=qtest-bn0,id=qtest-e0 " "-chardev socket,id=redirector0,path=%s,server,nowait " "-chardev socket,id=redirector1,path=%s,server,nowait " - "-chardev socket,id=redirector2,path=%s,nowait " + "-chardev socket,id=redirector2,path=%s " "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," "queue=tx,outdev=redirector0 " "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," @@ -166,7 +166,7 @@ static void test_redirector_rx(void) "-device %s,netdev=qtest-bn0,id=qtest-e0 " "-chardev socket,id=redirector0,path=%s,server,nowait " "-chardev socket,id=redirector1,path=%s,server,nowait " - "-chardev socket,id=redirector2,path=%s,nowait " + "-chardev socket,id=redirector2,path=%s " "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," "queue=rx,indev=redirector0 " "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 7d5f234646..d961bd09d1 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -519,7 +519,7 @@ static void test_server_create_chr(TestServer *server, const gchar *opt) Chardev *chr; chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); - chr = qemu_chr_new(server->chr_name, chr_path); + chr = qemu_chr_new(server->chr_name, chr_path, NULL); g_free(chr_path); g_assert_nonnull(chr); @@ -2302,7 +2302,7 @@ static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp) { Error *local_err = NULL; - if (!qemu_chr_new_from_opts(opts, &local_err)) { + if (!qemu_chr_new_from_opts(opts, NULL, &local_err)) { if (local_err) { error_propagate(errp, local_err); return -1; @@ -2440,7 +2440,7 @@ static int serial_parse(const char *devname) snprintf(label, sizeof(label), "serial%d", index); serial_hds = g_renew(Chardev *, serial_hds, index + 1); - serial_hds[index] = qemu_chr_new_mux_mon(label, devname); + serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!serial_hds[index]) { error_report("could not connect serial device" " to character backend '%s'", devname); @@ -2476,7 +2476,7 @@ static int parallel_parse(const char *devname) exit(1); } snprintf(label, sizeof(label), "parallel%d", index); - parallel_hds[index] = qemu_chr_new_mux_mon(label, devname); + parallel_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!parallel_hds[index]) { error_report("could not connect parallel device" " to character backend '%s'", devname); @@ -2490,7 +2490,7 @@ static int debugcon_parse(const char *devname) { QemuOpts *opts; - if (!qemu_chr_new_mux_mon("debugcon", devname)) { + if (!qemu_chr_new_mux_mon("debugcon", devname, NULL)) { error_report("invalid character backend '%s'", devname); exit(1); } |