summaryrefslogtreecommitdiffstats
path: root/migration/multifd.c
Commit message (Collapse)AuthorAgeFilesLines
* migration/multifd: Report to user when zerocopy not workingLeonardo Bras2022-07-201-0/+2
| | | | | | | | | | | | | | | | | | Some errors, like the lack of Scatter-Gather support by the network interface(NETIF_F_SG) may cause sendmsg(...,MSG_ZEROCOPY) to fail on using zero-copy, which causes it to fall back to the default copying mechanism. After each full dirty-bitmap scan there should be a zero-copy flush happening, which checks for errors each of the previous calls to sendmsg(...,MSG_ZEROCOPY). If all of them failed to use zero-copy, then increment dirty_sync_missed_zero_copy migration stat to let the user know about it. Signed-off-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Acked-by: Peter Xu <peterx@redhat.com> Message-Id: <20220711211112.18951-4-leobras@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: Add helpers to detect TLS capabilityPeter Xu2022-07-201-3/+1Star
| | | | | | | | | | | Add migrate_channel_requires_tls() to detect whether the specific channel requires TLS, leveraging the recently introduced migrate_use_tls(). No functional change intended. Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: Peter Xu <peterx@redhat.com> Message-Id: <20220707185513.27421-1-peterx@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: rename qemu_file_update_transfer to qemu_file_acct_rate_limitDaniel P. Berrangé2022-06-221-2/+2
| | | | | | | | | | | | | | The qemu_file_update_transfer name doesn't give a clear guide on what its purpose is, and how it differs from the qemu_file_credit_transfer method. The latter is specifically for accumulating for total migration traffic, while the former is specifically for accounting in thue rate limit calculations. The new name give better guidance on its usage. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Implement zero copy write in multifd migration (multifd-zero-copy)Leonardo Bras2022-05-161-2/+35
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Implement zero copy send on nocomp_send_write(), by making use of QIOChannel writev + flags & flush interface. Change multifd_send_sync_main() so flush_zero_copy() can be called after each iteration in order to make sure all dirty pages are sent before a new iteration is started. It will also flush at the beginning and at the end of migration. Also make it return -1 if flush_zero_copy() fails, in order to cancel the migration process, and avoid resuming the guest in the target host without receiving all current RAM. This will work fine on RAM migration because the RAM pages are not usually freed, and there is no problem on changing the pages content between writev_zero_copy() and the actual sending of the buffer, because this change will dirty the page and cause it to be re-sent on a next iteration anyway. A lot of locked memory may be needed in order to use multifd migration with zero-copy enabled, so disabling the feature should be necessary for low-privileged users trying to perform multifd migrations. Signed-off-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20220513062836.965425-9-leobras@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Send header packet without flags if zero-copy-send is enabledLeonardo Bras2022-05-161-3/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Since d48c3a0445 ("multifd: Use a single writev on the send side"), sending the header packet and the memory pages happens in the same writev, which can potentially make the migration faster. Using channel-socket as example, this works well with the default copying mechanism of sendmsg(), but with zero-copy-send=true, it will cause the migration to often break. This happens because the header packet buffer gets reused quite often, and there is a high chance that by the time the MSG_ZEROCOPY mechanism get to send the buffer, it has already changed, sending the wrong data and causing the migration to abort. It means that, as it is, the buffer for the header packet is not suitable for sending with MSG_ZEROCOPY. In order to enable zero copy for multifd, send the header packet on an individual write(), without any flags, and the remanining pages with a writev(), as it was happening before. This only changes how a migration with zero-copy-send=true works, not changing any current behavior for migrations with zero-copy-send=false. Signed-off-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20220513062836.965425-8-leobras@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: multifd_send_sync_main now returns negative on errorLeonardo Bras2022-05-161-4/+6
| | | | | | | | | | | | | | | | | | | Even though multifd_send_sync_main() currently emits error_reports, it's callers don't really check it before continuing. Change multifd_send_sync_main() to return -1 on error and 0 on success. Also change all it's callers to make use of this change and possibly fail earlier. (This change is important to next patch on multifd zero copy implementation, to make it sure an error in zero-copy flush does not go unnoticed. Signed-off-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Message-Id: <20220513062836.965425-7-leobras@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: Add migrate_use_tls() helperLeonardo Bras2022-05-161-4/+1Star
| | | | | | | | | | | | | | | A lot of places check parameters.tls_creds in order to evaluate if TLS is in use, and sometimes call migrate_get_current() just for that test. Add new helper function migrate_use_tls() in order to simplify testing for TLS usage. Signed-off-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20220513062836.965425-6-leobras@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: Move migrate_allow_multifd and helpers into migration.cPeter Xu2022-04-211-15/+4Star
| | | | | | | | | | | | | | This variable, along with its helpers, is used to detect whether multiple channel will be supported for migration. In follow up patches, there'll be other capability that requires multi-channels. Hence move it outside multifd specific code and make it public. Meanwhile rename it from "multifd" to "multi_channels" to show its real meaning. Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: Peter Xu <peterx@redhat.com> Message-Id: <20220331150857.74406-5-peterx@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: Drop multifd tls_hostname cachePeter Xu2022-04-211-7/+3Star
| | | | | | | | | | | | | | | The hostname is cached N times, N equals to the multifd channels. Drop that cache because after previous patch we've got s->hostname being alive for the whole lifecycle of migration procedure. Cc: Juan Quintela <quintela@redhat.com> Cc: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Peter Xu <peterx@redhat.com> Message-Id: <20220331150857.74406-3-peterx@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Rename pages_used to normal_pagesJuan Quintela2022-01-281-2/+2
| | | | | Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: recv side only needs the RAMBlock host addressJuan Quintela2022-01-281-5/+2Star
| | | | | | | So we can remove the MultiFDPages. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Use normal pages array on the recv sideJuan Quintela2022-01-281-25/+20Star
| | | | | | | | | Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> --- Rename num_normal_pages to total_normal_pages (peter)
* multifd: Use normal pages array on the send sideJuan Quintela2022-01-281-11/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We are only sending normal pages through multifd channels. Later on this series, we are going to also send zero pages. We are going to detect if a page is zero or non zero in the multifd channel thread, not on the main thread. So we receive an array of pages page->offset[N] And we will end with: p->normal[N - zero_pages] p->zero[zero_pages]. In this patch, we just copy all the pages in offset to normal. for (i = 0; i < pages->num; i++) { p->narmal[p->normal_num] = pages->offset[i]; p->normal_num++: } Later in the series this becomes: for (i = 0; i < pages->num; i++) { if (buffer_is_zero(page->offset[i])) { p->zerol[p->zero_num] = pages->offset[i]; p->zero_num++: } else { p->narmal[p->normal_num] = pages->offset[i]; p->normal_num++: } } Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> --- Improving comment (dave) Renaming num_normal_pages to total_normal_pages (peter)
* multifd: Unfold "used" variable by its valueJuan Quintela2022-01-281-5/+3Star
| | | | | Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Use a single writev on the send sideJuan Quintela2022-01-281-12/+8Star
| | | | | | | | | Until now, we wrote the packet header with write(), and the rest of the pages with writev(). Just increase the size of the iovec and do a single writev(). Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Remove send_write() methodJuan Quintela2022-01-281-18/+2Star
| | | | | | | Everything use now iov's. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Move iov from pages to paramsJuan Quintela2022-01-281-10/+24
| | | | | | This will allow us to reduce the number of system calls on the next patch. Signed-off-by: Juan Quintela <quintela@redhat.com>
* migration: All this fields are unsignedJuan Quintela2022-01-281-8/+8
| | | | | | | | | So printing it as %d is wrong. Notice that for the channel id, that is an uint8_t, but I changed it anyways for consistency. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com>
* multifd: Shut down the QIO channels to avoid blocking the send threads when ↵Li Zhang2021-12-151-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | they are terminated. When doing live migration with multifd channels 8, 16 or larger number, the guest hangs in the presence of the network errors such as missing TCP ACKs. At sender's side: The main thread is blocked on qemu_thread_join, migration_fd_cleanup is called because one thread fails on qio_channel_write_all when the network problem happens and other send threads are blocked on sendmsg. They could not be terminated. So the main thread is blocked on qemu_thread_join to wait for the threads terminated. (gdb) bt 0 0x00007f30c8dcffc0 in __pthread_clockjoin_ex () at /lib64/libpthread.so.0 1 0x000055cbb716084b in qemu_thread_join (thread=0x55cbb881f418) at ../util/qemu-thread-posix.c:627 2 0x000055cbb6b54e40 in multifd_save_cleanup () at ../migration/multifd.c:542 3 0x000055cbb6b4de06 in migrate_fd_cleanup (s=0x55cbb8024000) at ../migration/migration.c:1808 4 0x000055cbb6b4dfb4 in migrate_fd_cleanup_bh (opaque=0x55cbb8024000) at ../migration/migration.c:1850 5 0x000055cbb7173ac1 in aio_bh_call (bh=0x55cbb7eb98e0) at ../util/async.c:141 6 0x000055cbb7173bcb in aio_bh_poll (ctx=0x55cbb7ebba80) at ../util/async.c:169 7 0x000055cbb715ba4b in aio_dispatch (ctx=0x55cbb7ebba80) at ../util/aio-posix.c:381 8 0x000055cbb7173ffe in aio_ctx_dispatch (source=0x55cbb7ebba80, callback=0x0, user_data=0x0) at ../util/async.c:311 9 0x00007f30c9c8cdf4 in g_main_context_dispatch () at /usr/lib64/libglib-2.0.so.0 10 0x000055cbb71851a2 in glib_pollfds_poll () at ../util/main-loop.c:232 11 0x000055cbb718521c in os_host_main_loop_wait (timeout=42251070366) at ../util/main-loop.c:255 12 0x000055cbb7185321 in main_loop_wait (nonblocking=0) at ../util/main-loop.c:531 13 0x000055cbb6e6ba27 in qemu_main_loop () at ../softmmu/runstate.c:726 14 0x000055cbb6ad6fd7 in main (argc=68, argv=0x7ffc0c578888, envp=0x7ffc0c578ab0) at ../softmmu/main.c:50 To make sure that the send threads could be terminated, IO channels should be shut down to avoid waiting IO. Signed-off-by: Li Zhang <lizhang@suse.de> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com>
* multifd: Fill offset and block for receptionJuan Quintela2021-12-151-0/+2
| | | | | | | | We were using the iov directly, but we will need this info on the following patch. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: remove used parameter from send_recv_pages() methodJuan Quintela2021-12-151-4/+3Star
| | | | | | | It is already there as p->pages->num. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: remove used parameter from send_prepare() methodJuan Quintela2021-12-151-6/+3Star
| | | | | | | It is already there as p->pages->num. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: The variable is only used inside the loopJuan Quintela2021-12-151-2/+1Star
| | | | | Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Add missing documentionJuan Quintela2021-12-151-0/+1
| | | | | Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* multifd: Rename used field to numJuan Quintela2021-12-151-19/+19
| | | | | | | | We will need to split it later in zero_num (number of zero pages) and normal_num (number of normal pages). This name is better. Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: Never call twice qemu_target_page_size()Juan Quintela2021-12-151-3/+4
| | | | | Signed-off-by: Juan Quintela <quintela@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: allow enabling mutilfd for specific protocol onlyLi Zhijian2021-10-191-1/+1
| | | | | | | | | | | | | To: <quintela@redhat.com>, <dgilbert@redhat.com>, <qemu-devel@nongnu.org> CC: Li Zhijian <lizhijian@cn.fujitsu.com> Date: Sat, 31 Jul 2021 22:05:52 +0800 (5 weeks, 4 days, 17 hours ago) And change the default to true so that in '-incoming defer' case, user is able to change multifd capability. Signed-off-by: Li Zhijian <lizhijian@cn.fujitsu.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com>
* migration: allow multifd for socket protocol onlyLi Zhijian2021-10-191-2/+22
| | | | | | | | | | | | | | | | | | | | | | | | | To: <quintela@redhat.com>, <dgilbert@redhat.com>, <qemu-devel@nongnu.org> CC: Li Zhijian <lizhijian@cn.fujitsu.com> Date: Sat, 31 Jul 2021 22:05:51 +0800 (5 weeks, 4 days, 17 hours ago) multifd with unsupported protocol will cause a segment fault. (gdb) bt #0 0x0000563b4a93faf8 in socket_connect (addr=0x0, errp=0x7f7f02675410) at ../util/qemu-sockets.c:1190 #1 0x0000563b4a797a03 in qio_channel_socket_connect_sync (ioc=0x563b4d16e8c0, addr=0x0, errp=0x7f7f02675410) at ../io/channel-socket.c:145 #2 0x0000563b4a797abf in qio_channel_socket_connect_worker (task=0x563b4cd86c30, opaque=0x0) at ../io/channel-socket.c:168 #3 0x0000563b4a792631 in qio_task_thread_worker (opaque=0x563b4cd86c30) at ../io/task.c:124 #4 0x0000563b4a91da69 in qemu_thread_start (args=0x563b4c44bb80) at ../util/qemu-thread-posix.c:541 #5 0x00007f7fe9b5b3f9 in ?? () #6 0x0000000000000000 in ?? () It's enough to check migrate_multifd_is_allowed() in multifd cleanup() and multifd setup() though there are so many other places using migrate_use_multifd(). Signed-off-by: Li Zhijian <lizhijian@cn.fujitsu.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com>
* multifd: Unconditionally unregister yank functionLukas Straub2021-10-191-4/+1Star
| | | | | | | | | | | | | | | | | | | | | | | To: qemu-devel <qemu-devel@nongnu.org> Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>, Juan Quintela <quintela@redhat.com>, Peter Xu <peterx@redhat.com>, Leonardo Bras Soares Passos <lsoaresp@redhat.com> Date: Wed, 4 Aug 2021 21:26:32 +0200 (5 weeks, 11 hours, 52 minutes ago) [[PGP Signed Part:No public key for 35AB0B289C5DB258 created at 2021-08-04T21:26:32+0200 using RSA]] Unconditionally unregister yank function in multifd_load_cleanup(). If it is not unregistered here, it will leak and cause a crash in yank_unregister_instance(). Now if the ioc is still in use afterwards, it will only lead to qemu not being able to recover from a hang related to that ioc. After checking the code, i am pretty sure that ref is always 1 when arriving here. So all this currently does is remove the unneeded check. Signed-off-by: Lukas Straub <lukasstraub2@web.de> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com>
* multifd: Implement yank for multifd send sideLukas Straub2021-10-191-1/+5
| | | | | | | | | | | | | | | | | | To: qemu-devel <qemu-devel@nongnu.org> Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>, Juan Quintela <quintela@redhat.com>, Peter Xu <peterx@redhat.com>, Leonardo Bras Soares Passos <lsoaresp@redhat.com> Date: Wed, 1 Sep 2021 17:58:57 +0200 (1 week, 15 hours, 17 minutes ago) [[PGP Signed Part:No public key for 35AB0B289C5DB258 created at 2021-09-01T17:58:57+0200 using RSA]] When introducing yank functionality in the migration code I forgot to cover the multifd send side. Signed-off-by: Lukas Straub <lukasstraub2@web.de> Tested-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Leonardo Bras <leobras@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com>
* migration: Introduce migration_ioc_[un]register_yank()Peter Xu2021-07-261-6/+2Star
| | | | | | | | | | | | There're plenty of places in migration/* that checks against either socket or tls typed ioc for yank operations. Provide two helpers to hide all these information. Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: Peter Xu <peterx@redhat.com> Message-Id: <20210722175841.938739-4-peterx@redhat.com> Reviewed-by: Lukas Straub <lukasstraub2@web.de> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration/socket: Close the listener at the endDr. David Alan Gilbert2021-06-081-0/+5
| | | | | | | | | | Delay closing the listener until the cleanup hook at the end; mptcp needs the listener to stay open while the other paths come in. Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20210421112834.107651-5-dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* yank: Unregister function when using TLS migrationLeonardo Bras2021-06-081-1/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | After yank feature was introduced in migration, whenever migration is started using TLS, the following error happens in both source and destination hosts: (qemu) qemu-kvm: ../util/yank.c:107: yank_unregister_instance: Assertion `QLIST_EMPTY(&entry->yankfns)' failed. This happens because of a missing yank_unregister_function() when using qio-channel-tls. Fix this by also allowing TYPE_QIO_CHANNEL_TLS object type to perform yank_unregister_function() in channel_close() and multifd_load_cleanup(). Also, inside migration_channel_connect() and migration_channel_process_incoming() move yank_register_function() so it only runs once on a TLS migration. Fixes: b5eea99ec2f ("migration: Add yank feature", 2021-01-13) Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1964326 Signed-off-by: Leonardo Bras <leobras.c@gmail.com> Reviewed-by: Lukas Straub <lukasstraub2@web.de> Reviewed-by: Peter Xu <peterx@redhat.com> -- Changes since v2: - Dropped all references to ioc->master - yank_register_function() and yank_unregister_function() now only run once in a TLS migration. Changes since v1: - Cast p->c to QIOChannelTLS into multifd_load_cleanup() Message-Id: <20210601054030.1153249-1-leobras.c@gmail.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration/multifd: Print used_length of memory blockDavid Hildenbrand2021-05-131-1/+1
| | | | | | | | | We actually want to print the used_length, against which we check. Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: David Hildenbrand <david@redhat.com> Message-Id: <20210429112708.12291-10-david@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* yank: Remove dependency on qiochannelLukas Straub2021-04-011-1/+2
| | | | | | | | | | Remove dependency on qiochannel by removing yank_generic_iochannel and letting migration and chardev use their own yank function for iochannel. Signed-off-by: Lukas Straub <lukasstraub2@web.de> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20ff143fc2db23e27cd41d38043e481376c9cec1.1616521341.git.lukasstraub2@web.de>
* migration/tls: add error handling in multifd_tls_handshake_threadHao Wang2021-03-151-1/+10
| | | | | | | | | | | | | | If any error happens during multifd send thread creating (e.g. channel broke because new domain is destroyed by the dst), multifd_tls_handshake_thread may exit silently, leaving main migration thread hanging (ram_save_setup -> multifd_send_sync_main -> qemu_sem_wait(&p->sem_sync)). Fix that by adding error handling in multifd_tls_handshake_thread. Signed-off-by: Hao Wang <wanghao232@huawei.com> Message-Id: <20210209104237.2250941-3-wanghao232@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration/tls: fix inverted semantics in multifd_channel_connectHao Wang2021-03-151-5/+5
| | | | | | | | | | | Function multifd_channel_connect() return "true" to indicate failure, which is rather confusing. Fix that. Signed-off-by: Hao Wang <wanghao232@huawei.com> Message-Id: <20210209104237.2250941-2-wanghao232@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration: Add yank featureLukas Straub2021-01-131-0/+10
| | | | | | | | | | Register yank functions on sockets to shut them down. Signed-off-by: Lukas Straub <lukasstraub2@web.de> Acked-by: Stefan Hajnoczi <stefanha@redhat.com> Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Message-Id: <484c6a14cc2506bebedd5a237259b91363ff8f88.1609167865.git.lukasstraub2@web.de> Signed-off-by: Markus Armbruster <armbru@redhat.com>
* multifd/tls: fix memoryleak of the QIOChannelSocket object when cancelling ↵Chuan Zheng2020-11-121-0/+1
| | | | | | | | | | | | | | migration When creating new tls client, the tioc->master will be referenced which results in socket leaking after multifd_save_cleanup if we cancel migration. Fix it by do object_unref() after tls client creation. Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Chuan Zheng <zhengchuan@huawei.com> Message-Id: <1605104763-118687-1-git-send-email-zhengchuan@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration/multifd: fix hangup with TLS-Multifd due to blocking handshakeChuan Zheng2020-11-121-6/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | The qemu main loop could hang up forever when we enable TLS+Multifd. The Src multifd_send_0 invokes tls handshake, it sends hello to sever and wait response. However, the Dst main qemu loop has been waiting recvmsg() for multifd_recv_1. Both of Src and Dst main qemu loop are blocking and waiting for reponse which results in hanging up forever. Src: (multifd_send_0) Dst: (multifd_recv_1) multifd_channel_connect migration_channel_process_incoming multifd_tls_channel_connect migration_tls_channel_process_incoming multifd_tls_channel_connect qio_channel_tls_handshake_task qio_channel_tls_handshake gnutls_handshake qio_channel_tls_handshake_task ... qcrypto_tls_session_handshake ... gnutls_handshake ... ... ... recvmsg (Blocking I/O waiting for response) recvmsg (Blocking I/O waiting for response) Fix this by offloadinig handshake work to a background thread. Reported-by: Yan Jin <jinyan12@huawei.com> Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Chuan Zheng <zhengchuan@huawei.com> Message-Id: <1604643893-8223-1-git-send-email-zhengchuan@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* Merge remote-tracking branch ↵Peter Maydell2020-09-281-1/+1
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 'remotes/vivier2/tags/trivial-branch-for-5.2-pull-request' into staging Trivial Patches Pull request 20200928 # gpg: Signature made Mon 28 Sep 2020 10:15:00 BST # gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C # gpg: issuer "laurent@vivier.eu" # gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full] # gpg: aka "Laurent Vivier <laurent@vivier.eu>" [full] # gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full] # Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C * remotes/vivier2/tags/trivial-branch-for-5.2-pull-request: docs/system/deprecated: Move lm32 and unicore32 to the right section migration/multifd: Remove superfluous semicolons timer: Fix timer_mod_anticipate() documentation vhost-vdpa: remove useless variable Add *.pyc back to the .gitignore file virtio: vdpa: omit check return of g_malloc meson: fix static flag summary vhost-vdpa: fix indentation in vdpa_ops Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
| * migration/multifd: Remove superfluous semicolonsPhilippe Mathieu-Daudé2020-09-231-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | checkpatch.pl report superfluous semicolons since commit ee0f3c09e01, but this one was missed: scripts/checkpatch.pl d32ca5ad798~..d32ca5ad798 ERROR: superfluous trailing semicolon #498: FILE: migration/multifd.c:308: + ram_counters.transferred += transferred;; total: 1 errors, 1 warnings, 2073 lines checked Fixes: d32ca5ad798 ("multifd: Split multifd code into its own file") Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Li Qiang <liq3ea@gmail.com> Message-Id: <20200921040231.437653-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
* | migration/tls: add trace points for multifd-tlsChuan Zheng2020-09-251-1/+9
| | | | | | | | | | | | | | | | | | | | add trace points for multifd-tls for debug. Signed-off-by: Chuan Zheng <zhengchuan@huawei.com> Signed-off-by: Yan Jin <jinyan12@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <1600139042-104593-7-git-send-email-zhengchuan@huawei.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* | migration/tls: add support for multifd tls-handshakeChuan Zheng2020-09-251-2/+75
| | | | | | | | | | | | | | | | | | | | | | Similar like migration main thread, we need to do handshake for each multifd thread. Signed-off-by: Chuan Zheng <zhengchuan@huawei.com> Signed-off-by: Yan Jin <jinyan12@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <1600139042-104593-6-git-send-email-zhengchuan@huawei.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* | migration/tls: extract cleanup function for common-useChuan Zheng2020-09-251-12/+22
| | | | | | | | | | | | | | | | | | | | | | multifd channel cleanup is need if multifd handshake failed, let's extract it. Signed-off-by: Chuan Zheng <zhengchuan@huawei.com> Signed-off-by: Yan Jin <jinyan12@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <1600139042-104593-5-git-send-email-zhengchuan@huawei.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* | migration/tls: add tls_hostname into MultiFDSendParamsChuan Zheng2020-09-251-0/+5
| | | | | | | | | | | | | | | | | | | | | | Since multifd creation is async with migration_channel_connect, we should pass the hostname from MigrationState to MultiFDSendParams. Signed-off-by: Chuan Zheng <zhengchuan@huawei.com> Signed-off-by: Yan Jin <jinyan12@huawei.com> Message-Id: <1600139042-104593-4-git-send-email-zhengchuan@huawei.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* | qemu/atomic.h: rename atomic_ to qatomic_Stefan Hajnoczi2020-09-231-9/+9
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | clang's C11 atomic_fetch_*() functions only take a C11 atomic type pointer argument. QEMU uses direct types (int, etc) and this causes a compiler error when a QEMU code calls these functions in a source file that also included <stdatomic.h> via a system header file: $ CC=clang CXX=clang++ ./configure ... && make ../util/async.c:79:17: error: address argument to atomic operation must be a pointer to _Atomic type ('unsigned int *' invalid) Avoid using atomic_*() names in QEMU's atomic.h since that namespace is used by <stdatomic.h>. Prefix QEMU's APIs with 'q' so that atomic.h and <stdatomic.h> can co-exist. I checked /usr/include on my machine and searched GitHub for existing "qatomic_" users but there seem to be none. This patch was generated using: $ git grep -h -o '\<atomic\(64\)\?_[a-z0-9_]\+' include/qemu/atomic.h | \ sort -u >/tmp/changed_identifiers $ for identifier in $(</tmp/changed_identifiers); do sed -i "s%\<$identifier\>%q$identifier%g" \ $(git grep -I -l "\<$identifier\>") done I manually fixed line-wrap issues and misaligned rST tables. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Message-Id: <20200923105646.47864-1-stefanha@redhat.com>
* migration/: fix some comment spelling errorszhaolichang2020-09-171-2/+2
| | | | | | | | | | | I found that there are many spelling errors in the comments of qemu, so I used the spellcheck tool to check the spelling errors and finally found some spelling errors in the migration folder. Signed-off-by: zhaolichang <zhaolichang@huawei.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-Id: <20200917075029.313-3-zhaolichang@huawei.com> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
* migration: fix multifd_send_pages() next channelLaurent Vivier2020-06-171-0/+6
| | | | | | | | | | | | | | | | | | | | | | | multifd_send_pages() loops around the available channels, the next channel to use between two calls to multifd_send_pages() is stored inside a local static variable, next_channel. It works well, except if the number of channels decreases between two calls to multifd_send_pages(). In this case, the loop can try to access the data of a channel that doesn't exist anymore. The problem can be triggered if we start a migration with a given number of channels and then we cancel the migration to restart it with a lower number. This ends generally with an error like: qemu-system-ppc64: .../util/qemu-thread-posix.c:77: qemu_mutex_lock_impl: Assertion `mutex->initialized' failed. This patch fixes the error by capping next_channel with the current number of channels before using it. Signed-off-by: Laurent Vivier <lvivier@redhat.com> Message-Id: <20200617113154.593233-1-lvivier@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
* migration/multifd: Do error_free after migrate_set_error to avoid memleaksPan Nengyuan2020-05-071-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When error happen in multifd_send_thread, it use error_copy to set migrate error in multifd_send_terminate_threads(). We should call error_free after it. Similarly, fix another two places in multifd_recv_thread/multifd_save_cleanup. The leak stack: Direct leak of 48 byte(s) in 1 object(s) allocated from: #0 0x7f781af07cf0 in calloc (/lib64/libasan.so.5+0xefcf0) #1 0x7f781a2ce22d in g_malloc0 (/lib64/libglib-2.0.so.0+0x5322d) #2 0x55ee1d075c17 in error_setv /mnt/sdb/backup/qemu/util/error.c:61 #3 0x55ee1d076464 in error_setg_errno_internal /mnt/sdb/backup/qemu/util/error.c:109 #4 0x55ee1cef066e in qio_channel_socket_writev /mnt/sdb/backup/qemu/io/channel-socket.c:569 #5 0x55ee1cee806b in qio_channel_writev /mnt/sdb/backup/qemu/io/channel.c:207 #6 0x55ee1cee806b in qio_channel_writev_all /mnt/sdb/backup/qemu/io/channel.c:171 #7 0x55ee1cee8248 in qio_channel_write_all /mnt/sdb/backup/qemu/io/channel.c:257 #8 0x55ee1ca12c9a in multifd_send_thread /mnt/sdb/backup/qemu/migration/multifd.c:657 #9 0x55ee1d0607fc in qemu_thread_start /mnt/sdb/backup/qemu/util/qemu-thread-posix.c:519 #10 0x7f78159ae2dd in start_thread (/lib64/libpthread.so.0+0x82dd) #11 0x7f78156df4b2 in __GI___clone (/lib64/libc.so.6+0xfc4b2) Indirect leak of 52 byte(s) in 1 object(s) allocated from: #0 0x7f781af07f28 in __interceptor_realloc (/lib64/libasan.so.5+0xeff28) #1 0x7f78156f07d9 in __GI___vasprintf_chk (/lib64/libc.so.6+0x10d7d9) #2 0x7f781a30ea6c in g_vasprintf (/lib64/libglib-2.0.so.0+0x93a6c) #3 0x7f781a2e7cd0 in g_strdup_vprintf (/lib64/libglib-2.0.so.0+0x6ccd0) #4 0x7f781a2e7d8c in g_strdup_printf (/lib64/libglib-2.0.so.0+0x6cd8c) #5 0x55ee1d075c86 in error_setv /mnt/sdb/backup/qemu/util/error.c:65 #6 0x55ee1d076464 in error_setg_errno_internal /mnt/sdb/backup/qemu/util/error.c:109 #7 0x55ee1cef066e in qio_channel_socket_writev /mnt/sdb/backup/qemu/io/channel-socket.c:569 #8 0x55ee1cee806b in qio_channel_writev /mnt/sdb/backup/qemu/io/channel.c:207 #9 0x55ee1cee806b in qio_channel_writev_all /mnt/sdb/backup/qemu/io/channel.c:171 #10 0x55ee1cee8248 in qio_channel_write_all /mnt/sdb/backup/qemu/io/channel.c:257 #11 0x55ee1ca12c9a in multifd_send_thread /mnt/sdb/backup/qemu/migration/multifd.c:657 #12 0x55ee1d0607fc in qemu_thread_start /mnt/sdb/backup/qemu/util/qemu-thread-posix.c:519 #13 0x7f78159ae2dd in start_thread (/lib64/libpthread.so.0+0x82dd) #14 0x7f78156df4b2 in __GI___clone (/lib64/libc.so.6+0xfc4b2) Reported-by: Euler Robot <euler.robot@huawei.com> Signed-off-by: Pan Nengyuan <pannengyuan@huawei.com> Message-Id: <20200506095416.26099-3-pannengyuan@huawei.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
id=febc9920c66dd51245c2ca2ee88ada2aa5d0a63e'>^
40e9eddd38 ^


40e9eddd38 ^
cf7e0c80aa ^
40e9eddd38 ^























cf7e0c80aa ^
40e9eddd38 ^





5cabc5ccfe ^


40e9eddd38 ^

4ffb9ae2e1 ^



40e9eddd38 ^


d1a94fec3d ^
40e9eddd38 ^
e1ccc05444 ^

e1ccc05444 ^





































40e9eddd38 ^
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
















                                                                    
                                                                               

   
                       



                                   



                                 



                                    



                                        



                                                                   







                 











                                                      
                                                                      


                                      


                                                                            
                                   





























                                                                
                                                        























                                                                     







                                                               
                                               

































                                                                             

                                                                 
 
                    




                     

                                                      
                                                                      





                                 
                                                         
                    
                                                         

                                 
                                                         
                    
                                                         

              
                                                

                                  
 




                                                            
         


















                                                                          



































































                                                                         
                                                                        






























                                                              


















                                                                     
                                                            






























                                                              

                                                        








                                                                            
                                                                          













































































































                                                                               
            
 
                                                  
 




                                                       
 

                                                                    









                                                  
                                       













                                                                    
                       
 
                                                  
 

                                                                        
 
                        




































































































                                                                              
                            
                                                                  
                              















                                                                                
                                                            




















































                                                                           
                                                                        




















                                                                            
                                                            





                      

                                                                           








                                                   
                                                       



















                                                                           
                                                  




                                               
                                                                            








                                                                  
                                                    


























                                                                  
                                          

                          
                                                       





















                                                                         
                                           
                
                                                 



































                                                     






                                                    

                                                             





                              
                                                          












                                                            

                                                               





                             
                                                       











                                                               
                                                                    











                                                                         
                                                        











                                                      
                                                         






                                                    
                                            













                                                               
                                                                      












                                                          
                                                          








                                                                 
                                                        
                                                                       




                                                                    
                                                                  









                                             
                                                         

                                                    


                                                                           
                                                                    






                                                                            
                                                                      







                                                                           
                                                                    



                                                                           
                                                                    



                                                                             
                                                           



                                                                           
                                                                    



                                                                          
                                                                   

                                  
                                                             

                                  
                                                            

                                  
                                                            

                                  
                                                            







                                                                       
                                                       

                      






















                                                                               

                                                                      
                                                            






                                    
                                                    





                                               
                                                            
                                                       
                                                    



                                                                                

                                                 





                                                                   
                                                                             


































                                                                                
                                                         

                              
                                                        

                              
                                                        

                              
                                                        

                             
                                          



                                
                                                  







                                                                         
                                                        





                    
                                                                        


                              
                                                         
                                        























                                                        
                                          





                                                                    


                                            

     



                                                  


                    
                                      
 

          





































                                                                          
 
/*
 *  CRISv10 emulation for qemu: main translation routines.
 *
 *  Copyright (c) 2010 AXIS Communications AB
 *  Written by Edgar E. Iglesias.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

#include "qemu/osdep.h"
#include "crisv10-decode.h"

static const char *regnames_v10[] =
{
    "$r0", "$r1", "$r2", "$r3",
    "$r4", "$r5", "$r6", "$r7",
    "$r8", "$r9", "$r10", "$r11",
    "$r12", "$r13", "$sp", "$pc",
};

static const char *pregnames_v10[] =
{
    "$bz", "$vr", "$p2", "$p3",
    "$wz", "$ccr", "$p6-prefix", "$mof",
    "$dz", "$ibr", "$irp", "$srp",
    "$bar", "$dccr", "$brp", "$usp",
};

/* We need this table to handle preg-moves with implicit width.  */
static int preg_sizes_v10[] = {
    1, /* bz.  */
    1, /* vr.  */
    1, /* pid. */
    1, /* srs. */
    2, /* wz.  */
    2, 2, 4,
    4, 4, 4, 4,
    4, 4, 4, 4,
};

static inline int dec10_size(unsigned int size)
{
    size++;
    if (size == 3)
        size++;
    return size;
}

static inline void cris_illegal_insn(DisasContext *dc)
{
    qemu_log_mask(LOG_GUEST_ERROR, "illegal insn at pc=%x\n", dc->pc);
    t_gen_raise_exception(EXCP_BREAK);
}

static void gen_store_v10_conditional(DisasContext *dc, TCGv addr, TCGv val,
                       unsigned int size, int mem_index)
{
    TCGLabel *l1 = gen_new_label();
    TCGv taddr = tcg_temp_local_new();
    TCGv tval = tcg_temp_local_new();
    TCGv t1 = tcg_temp_local_new();
    dc->postinc = 0;
    cris_evaluate_flags(dc);

    tcg_gen_mov_tl(taddr, addr);
    tcg_gen_mov_tl(tval, val);

    /* Store only if F flag isn't set */
    tcg_gen_andi_tl(t1, cpu_PR[PR_CCS], F_FLAG_V10);
    tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
    if (size == 1) {
        tcg_gen_qemu_st8(tval, taddr, mem_index);
    } else if (size == 2) {
        tcg_gen_qemu_st16(tval, taddr, mem_index);
    } else {
        tcg_gen_qemu_st32(tval, taddr, mem_index);
    }
    gen_set_label(l1);
    tcg_gen_shri_tl(t1, t1, 1);  /* shift F to P position */
    tcg_gen_or_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], t1); /*P=F*/
    tcg_temp_free(t1);
    tcg_temp_free(tval);
    tcg_temp_free(taddr);
}

static void gen_store_v10(DisasContext *dc, TCGv addr, TCGv val,
                       unsigned int size)
{
    int mem_index = cpu_mmu_index(&dc->cpu->env, false);

    /* If we get a fault on a delayslot we must keep the jmp state in
       the cpu-state to be able to re-execute the jmp.  */
    if (dc->delayed_branch == 1) {
        cris_store_direct_jmp(dc);
    }

    /* Conditional writes. We only support the kind were X is known
       at translation time.  */
    if (dc->flagx_known && dc->flags_x) {
        gen_store_v10_conditional(dc, addr, val, size, mem_index);
        return;
    }

    if (size == 1) {
        tcg_gen_qemu_st8(val, addr, mem_index);
    } else if (size == 2) {
        tcg_gen_qemu_st16(val, addr, mem_index);
    } else {
        tcg_gen_qemu_st32(val, addr, mem_index);
    }
}


/* Prefix flag and register are used to handle the more complex
   addressing modes.  */
static void cris_set_prefix(DisasContext *dc)
{
    dc->clear_prefix = 0;
    dc->tb_flags |= PFIX_FLAG;
    tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], PFIX_FLAG);

    /* prefix insns don't clear the x flag.  */
    dc->clear_x = 0;
    cris_lock_irq(dc);
}

static void crisv10_prepare_memaddr(DisasContext *dc,
                                    TCGv addr, unsigned int size)
{
    if (dc->tb_flags & PFIX_FLAG) {
        tcg_gen_mov_tl(addr, cpu_PR[PR_PREFIX]);
    } else {
        tcg_gen_mov_tl(addr, cpu_R[dc->src]);
    }
}

static unsigned int crisv10_post_memaddr(DisasContext *dc, unsigned int size)
{
    unsigned int insn_len = 0;

    if (dc->tb_flags & PFIX_FLAG) {
        if (dc->mode == CRISV10_MODE_AUTOINC) {
            tcg_gen_mov_tl(cpu_R[dc->src], cpu_PR[PR_PREFIX]);
        }
    } else {
        if (dc->mode == CRISV10_MODE_AUTOINC) {
            if (dc->src == 15) {
                insn_len += size & ~1;
            } else {
                tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], size);
            }
        }
    }
    return insn_len;
}

static int dec10_prep_move_m(CPUCRISState *env, DisasContext *dc,
                             int s_ext, int memsize, TCGv dst)
{
    unsigned int rs;
    uint32_t imm;
    int is_imm;
    int insn_len = 0;

    rs = dc->src;
    is_imm = rs == 15 && !(dc->tb_flags & PFIX_FLAG);
    LOG_DIS("rs=%d rd=%d is_imm=%d mode=%d pfix=%d\n",
             rs, dc->dst, is_imm, dc->mode, dc->tb_flags & PFIX_FLAG);

    /* Load [$rs] onto T1.  */
    if (is_imm) {
        if (memsize != 4) {
            if (s_ext) {
                if (memsize == 1)
                    imm = cpu_ldsb_code(env, dc->pc + 2);
                else
                    imm = cpu_ldsw_code(env, dc->pc + 2);
            } else {
                if (memsize == 1)
                    imm = cpu_ldub_code(env, dc->pc + 2);
                else
                    imm = cpu_lduw_code(env, dc->pc + 2);
            }
        } else
            imm = cpu_ldl_code(env, dc->pc + 2);

        tcg_gen_movi_tl(dst, imm);

        if (dc->mode == CRISV10_MODE_AUTOINC) {
            insn_len += memsize;
            if (memsize == 1)
                insn_len++;
            tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len);
        }
    } else {
        TCGv addr;

        addr = tcg_temp_new();
        cris_flush_cc_state(dc);
        crisv10_prepare_memaddr(dc, addr, memsize);
        gen_load(dc, dst, addr, memsize, 0);
        if (s_ext)
            t_gen_sext(dst, dst, memsize);
        else
            t_gen_zext(dst, dst, memsize);
        insn_len += crisv10_post_memaddr(dc, memsize);
        tcg_temp_free(addr);
    }

    if (dc->mode == CRISV10_MODE_INDIRECT && (dc->tb_flags & PFIX_FLAG)) {
        dc->dst = dc->src;
    }
    return insn_len;
}

static unsigned int dec10_quick_imm(DisasContext *dc)
{
    int32_t imm, simm;
    int op;

    /* sign extend.  */
    imm = dc->ir & ((1 << 6) - 1);
    simm = (int8_t) (imm << 2);
    simm >>= 2;
    switch (dc->opcode) {
        case CRISV10_QIMM_BDAP_R0:
        case CRISV10_QIMM_BDAP_R1:
        case CRISV10_QIMM_BDAP_R2:
        case CRISV10_QIMM_BDAP_R3:
            simm = (int8_t)dc->ir;
            LOG_DIS("bdap %d $r%d\n", simm, dc->dst);
            LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n",
                     dc->pc, dc->mode, dc->opcode, dc->src, dc->dst);
            cris_set_prefix(dc);
            if (dc->dst == 15) {
                tcg_gen_movi_tl(cpu_PR[PR_PREFIX], dc->pc + 2 + simm);
            } else {
                tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm);
            }
            break;

        case CRISV10_QIMM_MOVEQ:
            LOG_DIS("moveq %d, $r%d\n", simm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(simm), 4);
            break;
        case CRISV10_QIMM_CMPQ:
            LOG_DIS("cmpq %d, $r%d\n", simm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(simm), 4);
            break;
        case CRISV10_QIMM_ADDQ:
            LOG_DIS("addq %d, $r%d\n", imm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, CC_OP_ADD, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(imm), 4);
            break;
        case CRISV10_QIMM_ANDQ:
            LOG_DIS("andq %d, $r%d\n", simm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, CC_OP_AND, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(simm), 4);
            break;
        case CRISV10_QIMM_ASHQ:
            LOG_DIS("ashq %d, $r%d\n", simm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            op = imm & (1 << 5);
            imm &= 0x1f;
            if (op) {
                cris_alu(dc, CC_OP_ASR, cpu_R[dc->dst],
                          cpu_R[dc->dst], tcg_const_tl(imm), 4);
            } else {
                /* BTST */
                cris_update_cc_op(dc, CC_OP_FLAGS, 4);
                gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->dst],
                           tcg_const_tl(imm), cpu_PR[PR_CCS]);
            }
            break;
        case CRISV10_QIMM_LSHQ:
            LOG_DIS("lshq %d, $r%d\n", simm, dc->dst);

            op = CC_OP_LSL;
            if (imm & (1 << 5)) {
                op = CC_OP_LSR; 
            }
            imm &= 0x1f;
            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, op, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(imm), 4);
            break;
        case CRISV10_QIMM_SUBQ:
            LOG_DIS("subq %d, $r%d\n", imm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, CC_OP_SUB, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(imm), 4);
            break;
        case CRISV10_QIMM_ORQ:
            LOG_DIS("andq %d, $r%d\n", simm, dc->dst);

            cris_cc_mask(dc, CC_MASK_NZVC);
            cris_alu(dc, CC_OP_OR, cpu_R[dc->dst],
                     cpu_R[dc->dst], tcg_const_tl(simm), 4);
            break;

        case CRISV10_QIMM_BCC_R0:
        case CRISV10_QIMM_BCC_R1:
        case CRISV10_QIMM_BCC_R2:
        case CRISV10_QIMM_BCC_R3:
            imm = dc->ir & 0xff;
            /* bit 0 is a sign bit.  */
            if (imm & 1) {
                imm |= 0xffffff00;   /* sign extend.  */
                imm &= ~1;           /* get rid of the sign bit.  */
            }
            imm += 2;
            LOG_DIS("b%s %d\n", cc_name(dc->cond), imm);

            cris_cc_mask(dc, 0);
            cris_prepare_cc_branch(dc, imm, dc->cond); 
            break;

        default:
            LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n",
                     dc->pc, dc->mode, dc->opcode, dc->src, dc->dst);
            cpu_abort(CPU(dc->cpu), "Unhandled quickimm\n");
            break;
    }
    return 2;
}

static unsigned int dec10_setclrf(DisasContext *dc)
{
    uint32_t flags;
    unsigned int set = ~dc->opcode & 1;

    flags = EXTRACT_FIELD(dc->ir, 0, 3)
            | (EXTRACT_FIELD(dc->ir, 12, 15) << 4);
    LOG_DIS("%s set=%d flags=%x\n", __func__, set, flags);


    if (flags & X_FLAG) {
        dc->flagx_known = 1;
        if (set)
            dc->flags_x = X_FLAG;
        else
            dc->flags_x = 0;
    }

    cris_evaluate_flags (dc);
    cris_update_cc_op(dc, CC_OP_FLAGS, 4);
    cris_update_cc_x(dc);
    tcg_gen_movi_tl(cc_op, dc->cc_op);

    if (set) {
        tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags);
    } else {
        tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS],
                        ~(flags|F_FLAG_V10|P_FLAG_V10));
    }

    dc->flags_uptodate = 1;
    dc->clear_x = 0;
    cris_lock_irq(dc);
    return 2;
}

static inline void dec10_reg_prep_sext(DisasContext *dc, int size, int sext,
                                       TCGv dd, TCGv ds, TCGv sd, TCGv ss)
{
    if (sext) {
        t_gen_sext(dd, sd, size);
        t_gen_sext(ds, ss, size);
    } else {
        t_gen_zext(dd, sd, size);
        t_gen_zext(ds, ss, size);
    }
}

static void dec10_reg_alu(DisasContext *dc, int op, int size, int sext)
{
    TCGv t[2];

    t[0] = tcg_temp_new();
    t[1] = tcg_temp_new();
    dec10_reg_prep_sext(dc, size, sext,
                        t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]);

    if (op == CC_OP_LSL || op == CC_OP_LSR || op == CC_OP_ASR) {
        tcg_gen_andi_tl(t[1], t[1], 63);
    }

    assert(dc->dst != 15);
    cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], size);
    tcg_temp_free(t[0]);
    tcg_temp_free(t[1]);
}

static void dec10_reg_bound(DisasContext *dc, int size)
{
    TCGv t;

    t = tcg_temp_local_new();
    t_gen_zext(t, cpu_R[dc->src], size);
    cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[dc->dst], t, 4);
    tcg_temp_free(t);
}

static void dec10_reg_mul(DisasContext *dc, int size, int sext)
{
    int op = sext ? CC_OP_MULS : CC_OP_MULU;
    TCGv t[2];

    t[0] = tcg_temp_new();
    t[1] = tcg_temp_new();
    dec10_reg_prep_sext(dc, size, sext,
                        t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]);

    cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], 4);

    tcg_temp_free(t[0]);
    tcg_temp_free(t[1]);
}


static void dec10_reg_movs(DisasContext *dc)
{
    int size = (dc->size & 1) + 1;
    TCGv t;

    LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst);
    cris_cc_mask(dc, CC_MASK_NZVC);

    t = tcg_temp_new();
    if (dc->ir & 32)
        t_gen_sext(t, cpu_R[dc->src], size);
    else
        t_gen_zext(t, cpu_R[dc->src], size);

    cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, 4);
    tcg_temp_free(t);
}

static void dec10_reg_alux(DisasContext *dc, int op)
{
    int size = (dc->size & 1) + 1;
    TCGv t;

    LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst);
    cris_cc_mask(dc, CC_MASK_NZVC);

    t = tcg_temp_new();
    if (dc->ir & 32)
        t_gen_sext(t, cpu_R[dc->src], size);
    else
        t_gen_zext(t, cpu_R[dc->src], size);

    cris_alu(dc, op, cpu_R[dc->dst], cpu_R[dc->dst], t, 4);
    tcg_temp_free(t);
}

static void dec10_reg_mov_pr(DisasContext *dc)
{
    LOG_DIS("move p%d r%d sz=%d\n", dc->dst, dc->src, preg_sizes_v10[dc->dst]);
    cris_lock_irq(dc);
    if (dc->src == 15) {
        tcg_gen_mov_tl(env_btarget, cpu_PR[dc->dst]);
        cris_prepare_jmp(dc, JMP_INDIRECT);
        return;
    }
    if (dc->dst == PR_CCS) {
        cris_evaluate_flags(dc); 
    }
    cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src],
                 cpu_R[dc->src], cpu_PR[dc->dst], preg_sizes_v10[dc->dst]);
}

static void dec10_reg_abs(DisasContext *dc)
{
    TCGv t0;

    LOG_DIS("abs $r%u, $r%u\n", dc->src, dc->dst);

    assert(dc->dst != 15);
    t0 = tcg_temp_new();
    tcg_gen_sari_tl(t0, cpu_R[dc->src], 31);
    tcg_gen_xor_tl(cpu_R[dc->dst], cpu_R[dc->src], t0);
    tcg_gen_sub_tl(t0, cpu_R[dc->dst], t0);

    cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t0, 4);
    tcg_temp_free(t0);
}

static void dec10_reg_swap(DisasContext *dc)
{
    TCGv t0;

    LOG_DIS("not $r%d, $r%d\n", dc->src, dc->dst);

    cris_cc_mask(dc, CC_MASK_NZVC);
    t0 = tcg_temp_new();
    tcg_gen_mov_tl(t0, cpu_R[dc->src]);
    if (dc->dst & 8)
        tcg_gen_not_tl(t0, t0);
    if (dc->dst & 4)
        t_gen_swapw(t0, t0);
    if (dc->dst & 2)
        t_gen_swapb(t0, t0);
    if (dc->dst & 1)
        t_gen_swapr(t0, t0);
    cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src], cpu_R[dc->src], t0, 4);
    tcg_temp_free(t0);
}

static void dec10_reg_scc(DisasContext *dc)
{
    int cond = dc->dst;

    LOG_DIS("s%s $r%u\n", cc_name(cond), dc->src);

    gen_tst_cc(dc, cpu_R[dc->src], cond);
    tcg_gen_setcondi_tl(TCG_COND_NE, cpu_R[dc->src], cpu_R[dc->src], 0);

    cris_cc_mask(dc, 0);
}

static unsigned int dec10_reg(DisasContext *dc)
{
    TCGv t;
    unsigned int insn_len = 2;
    unsigned int size = dec10_size(dc->size);
    unsigned int tmp;

    if (dc->size != 3) {
        switch (dc->opcode) {
            case CRISV10_REG_MOVE_R:
                LOG_DIS("move.%d $r%d, $r%d\n", dc->size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_MOVE, size, 0);
                if (dc->dst == 15) {
                    tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
                    cris_prepare_jmp(dc, JMP_INDIRECT);
                    dc->delayed_branch = 1;
                }
                break;
            case CRISV10_REG_MOVX:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_movs(dc);
                break;
            case CRISV10_REG_ADDX:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alux(dc, CC_OP_ADD);
                break;
            case CRISV10_REG_SUBX:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alux(dc, CC_OP_SUB);
                break;
            case CRISV10_REG_ADD:
                LOG_DIS("add $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_ADD, size, 0);
                break;
            case CRISV10_REG_SUB:
                LOG_DIS("sub $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_SUB, size, 0);
                break;
            case CRISV10_REG_CMP:
                LOG_DIS("cmp $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_CMP, size, 0);
                break;
            case CRISV10_REG_BOUND:
                LOG_DIS("bound $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_bound(dc, size);
                break;
            case CRISV10_REG_AND:
                LOG_DIS("and $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_AND, size, 0);
                break;
            case CRISV10_REG_ADDI:
                if (dc->src == 15) {
                    /* nop.  */
                    return 2;
                }
                t = tcg_temp_new();
                LOG_DIS("addi r%d r%d size=%d\n", dc->src, dc->dst, dc->size);
                tcg_gen_shli_tl(t, cpu_R[dc->dst], dc->size & 3);
                tcg_gen_add_tl(cpu_R[dc->src], cpu_R[dc->src], t);
                tcg_temp_free(t);
                break;
            case CRISV10_REG_LSL:
                LOG_DIS("lsl $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_LSL, size, 0);
                break;
            case CRISV10_REG_LSR:
                LOG_DIS("lsr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_LSR, size, 0);
                break;
            case CRISV10_REG_ASR:
                LOG_DIS("asr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_ASR, size, 1);
                break;
            case CRISV10_REG_OR:
                LOG_DIS("or $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_OR, size, 0);
                break;
            case CRISV10_REG_NEG:
                LOG_DIS("neg $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_NEG, size, 0);
                break;
            case CRISV10_REG_BIAP:
                LOG_DIS("BIAP pc=%x reg %d r%d r%d size=%d\n", dc->pc,
                         dc->opcode, dc->src, dc->dst, size);
                switch (size) {
                    case 4: tmp = 2; break;
                    case 2: tmp = 1; break;
                    case 1: tmp = 0; break;
                    default:
                        cpu_abort(CPU(dc->cpu), "Unhandled BIAP");
                        break;
                }

                t = tcg_temp_new();
                tcg_gen_shli_tl(t, cpu_R[dc->dst], tmp);
                if (dc->src == 15) {
                    tcg_gen_addi_tl(cpu_PR[PR_PREFIX], t, ((dc->pc +2)| 1) + 1);
                } else {
                    tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_R[dc->src], t);
                }
                tcg_temp_free(t);
                cris_set_prefix(dc);
                break;

            default:
                LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc,
                         dc->opcode, dc->src, dc->dst);
                cpu_abort(CPU(dc->cpu), "Unhandled opcode");
                break;
        }
    } else {
        switch (dc->opcode) {
            case CRISV10_REG_MOVX:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_movs(dc);
                break;
            case CRISV10_REG_ADDX:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alux(dc, CC_OP_ADD);
                break;
            case CRISV10_REG_SUBX:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alux(dc, CC_OP_SUB);
                break;
            case CRISV10_REG_MOVE_SPR_R:
                cris_evaluate_flags(dc);
                cris_cc_mask(dc, 0);
                dec10_reg_mov_pr(dc);
                break;
            case CRISV10_REG_MOVE_R_SPR:
                LOG_DIS("move r%d p%d\n", dc->src, dc->dst);
                cris_evaluate_flags(dc);
                if (dc->src != 11) /* fast for srp.  */
                    dc->cpustate_changed = 1;
                t_gen_mov_preg_TN(dc, dc->dst, cpu_R[dc->src]);
                break;
            case CRISV10_REG_SETF:
            case CRISV10_REG_CLEARF:
                dec10_setclrf(dc);
                break;
            case CRISV10_REG_SWAP:
                dec10_reg_swap(dc);
                break;
            case CRISV10_REG_ABS:
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_abs(dc);
                break;
            case CRISV10_REG_LZ:
                LOG_DIS("lz $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_LZ, 4, 0);
                break;
            case CRISV10_REG_XOR:
                LOG_DIS("xor $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_alu(dc, CC_OP_XOR, 4, 0);
                break;
            case CRISV10_REG_BTST:
                LOG_DIS("btst $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                cris_update_cc_op(dc, CC_OP_FLAGS, 4);
                gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->dst],
                           cpu_R[dc->src], cpu_PR[PR_CCS]);
                break;
            case CRISV10_REG_DSTEP:
                LOG_DIS("dstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_cc_mask(dc, CC_MASK_NZVC);
                cris_alu(dc, CC_OP_DSTEP, cpu_R[dc->dst],
                            cpu_R[dc->dst], cpu_R[dc->src], 4);
                break;
            case CRISV10_REG_MSTEP:
                LOG_DIS("mstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
                cris_evaluate_flags(dc);
                cris_cc_mask(dc, CC_MASK_NZVC);
                cris_alu(dc, CC_OP_MSTEP, cpu_R[dc->dst],
                            cpu_R[dc->dst], cpu_R[dc->src], 4);
                break;
            case CRISV10_REG_SCC:
                dec10_reg_scc(dc);
                break;
            default:
                LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc,
                         dc->opcode, dc->src, dc->dst);
                cpu_abort(CPU(dc->cpu), "Unhandled opcode");
                break;
        }
    }
    return insn_len;
}

static unsigned int dec10_ind_move_m_r(CPUCRISState *env, DisasContext *dc,
                                       unsigned int size)
{
    unsigned int insn_len = 2;
    TCGv t;

    LOG_DIS("%s: move.%d [$r%d], $r%d\n", __func__,
             size, dc->src, dc->dst);

    cris_cc_mask(dc, CC_MASK_NZVC);
    t = tcg_temp_new();
    insn_len += dec10_prep_move_m(env, dc, 0, size, t);
    cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, size);
    if (dc->dst == 15) {
        tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
        cris_prepare_jmp(dc, JMP_INDIRECT);
        dc->delayed_branch = 1;
        return insn_len;
    }

    tcg_temp_free(t);
    return insn_len;
}

static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size)
{
    unsigned int insn_len = 2;
    TCGv addr;

    LOG_DIS("move.%d $r%d, [$r%d]\n", dc->size, dc->src, dc->dst);
    addr = tcg_temp_new();
    crisv10_prepare_memaddr(dc, addr, size);
    gen_store_v10(dc, addr, cpu_R[dc->dst], size);
    insn_len += crisv10_post_memaddr(dc, size);

    return insn_len;
}

static unsigned int dec10_ind_move_m_pr(CPUCRISState *env, DisasContext *dc)
{
    unsigned int insn_len = 2, rd = dc->dst;
    TCGv t, addr;

    LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src);
    cris_lock_irq(dc);

    addr = tcg_temp_new();
    t = tcg_temp_new();
    insn_len += dec10_prep_move_m(env, dc, 0, 4, t);
    if (rd == 15) {
        tcg_gen_mov_tl(env_btarget, t);
        cris_prepare_jmp(dc, JMP_INDIRECT);
        dc->delayed_branch = 1;
        return insn_len;
    }

    tcg_gen_mov_tl(cpu_PR[rd], t);
    dc->cpustate_changed = 1;
    tcg_temp_free(addr);
    tcg_temp_free(t);
    return insn_len;
}

static unsigned int dec10_ind_move_pr_m(DisasContext *dc)
{
    unsigned int insn_len = 2, size = preg_sizes_v10[dc->dst];
    TCGv addr, t0;

    LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src);

    addr = tcg_temp_new();
    crisv10_prepare_memaddr(dc, addr, size);
    if (dc->dst == PR_CCS) {
        t0 = tcg_temp_new();
        cris_evaluate_flags(dc);
        tcg_gen_andi_tl(t0, cpu_PR[PR_CCS], ~PFIX_FLAG);
        gen_store_v10(dc, addr, t0, size);
        tcg_temp_free(t0);
    } else {
        gen_store_v10(dc, addr, cpu_PR[dc->dst], size);
    }
    t0 = tcg_temp_new();
    insn_len += crisv10_post_memaddr(dc, size);
    cris_lock_irq(dc);

    return insn_len;
}

static void dec10_movem_r_m(DisasContext *dc)
{
    int i, pfix = dc->tb_flags & PFIX_FLAG;
    TCGv addr, t0;

    LOG_DIS("%s r%d, [r%d] pi=%d ir=%x\n", __func__,
              dc->dst, dc->src, dc->postinc, dc->ir);

    addr = tcg_temp_new();
    t0 = tcg_temp_new();
    crisv10_prepare_memaddr(dc, addr, 4);
    tcg_gen_mov_tl(t0, addr);
    for (i = dc->dst; i >= 0; i--) {
        if ((pfix && dc->mode == CRISV10_MODE_AUTOINC) && dc->src == i) {
            gen_store_v10(dc, addr, t0, 4);
        } else {
            gen_store_v10(dc, addr, cpu_R[i], 4);
        }
        tcg_gen_addi_tl(addr, addr, 4);
    }

    if (pfix && dc->mode == CRISV10_MODE_AUTOINC) {
        tcg_gen_mov_tl(cpu_R[dc->src], t0);
    }

    if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) {
        tcg_gen_mov_tl(cpu_R[dc->src], addr);
    }
    tcg_temp_free(addr);
    tcg_temp_free(t0);
}

static void dec10_movem_m_r(DisasContext *dc)
{
    int i, pfix = dc->tb_flags & PFIX_FLAG;
    TCGv addr, t0;

    LOG_DIS("%s [r%d], r%d pi=%d ir=%x\n", __func__,
              dc->src, dc->dst, dc->postinc, dc->ir);

    addr = tcg_temp_new();
    t0 = tcg_temp_new();
    crisv10_prepare_memaddr(dc, addr, 4);
    tcg_gen_mov_tl(t0, addr);
    for (i = dc->dst; i >= 0; i--) {
        gen_load(dc, cpu_R[i], addr, 4, 0);
        tcg_gen_addi_tl(addr, addr, 4);
    }

    if (pfix && dc->mode == CRISV10_MODE_AUTOINC) {
        tcg_gen_mov_tl(cpu_R[dc->src], t0);
    }

    if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) {
        tcg_gen_mov_tl(cpu_R[dc->src], addr);
    }
    tcg_temp_free(addr);
    tcg_temp_free(t0);
}

static int dec10_ind_alu(CPUCRISState *env, DisasContext *dc,
                         int op, unsigned int size)
{
    int insn_len = 0;
    int rd = dc->dst;
    TCGv t[2];

    cris_alu_m_alloc_temps(t);
    insn_len += dec10_prep_move_m(env, dc, 0, size, t[0]);
    cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t[0], size);
    if (dc->dst == 15) {
        tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
        cris_prepare_jmp(dc, JMP_INDIRECT);
        dc->delayed_branch = 1;
        return insn_len;
    }

    cris_alu_m_free_temps(t);

    return insn_len;
}

static int dec10_ind_bound(CPUCRISState *env, DisasContext *dc,
                           unsigned int size)
{
    int insn_len = 0;
    int rd = dc->dst;
    TCGv t;

    t = tcg_temp_local_new();
    insn_len += dec10_prep_move_m(env, dc, 0, size, t);
    cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[rd], t, 4);
    if (dc->dst == 15) {
        tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
        cris_prepare_jmp(dc, JMP_INDIRECT);
        dc->delayed_branch = 1;
        return insn_len;
    }

    tcg_temp_free(t);
    return insn_len;
}

static int dec10_alux_m(CPUCRISState *env, DisasContext *dc, int op)
{
    unsigned int size = (dc->size & 1) ? 2 : 1;
    unsigned int sx = !!(dc->size & 2);
    int insn_len = 2;
    int rd = dc->dst;
    TCGv t;

    LOG_DIS("addx size=%d sx=%d op=%d %d\n", size, sx, dc->src, dc->dst);

    t = tcg_temp_new();

    cris_cc_mask(dc, CC_MASK_NZVC);
    insn_len += dec10_prep_move_m(env, dc, sx, size, t);
    cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t, 4);
    if (dc->dst == 15) {
        tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
        cris_prepare_jmp(dc, JMP_INDIRECT);
        dc->delayed_branch = 1;
        return insn_len;
    }

    tcg_temp_free(t);
    return insn_len;
}

static int dec10_dip(CPUCRISState *env, DisasContext *dc)
{
    int insn_len = 2;
    uint32_t imm;

    LOG_DIS("dip pc=%x opcode=%d r%d r%d\n",
              dc->pc, dc->opcode, dc->src, dc->dst);
    if (dc->src == 15) {
        imm = cpu_ldl_code(env, dc->pc + 2);
        tcg_gen_movi_tl(cpu_PR[PR_PREFIX], imm);
        if (dc->postinc)
            insn_len += 4;
        tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len - 2);
    } else {
        gen_load(dc, cpu_PR[PR_PREFIX], cpu_R[dc->src], 4, 0);
        if (dc->postinc)
            tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], 4);
    }

    cris_set_prefix(dc);
    return insn_len;
}

static int dec10_bdap_m(CPUCRISState *env, DisasContext *dc, int size)
{
    int insn_len = 2;
    int rd = dc->dst;

    LOG_DIS("bdap_m pc=%x opcode=%d r%d r%d sz=%d\n",
              dc->pc, dc->opcode, dc->src, dc->dst, size);

    assert(dc->dst != 15);
#if 0
    /* 8bit embedded offset?  */
    if (!dc->postinc && (dc->ir & (1 << 11))) {
        int simm = dc->ir & 0xff;

        /* cpu_abort(CPU(dc->cpu), "Unhandled opcode"); */
        /* sign extended.  */
        simm = (int8_t)simm;

        tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm);

        cris_set_prefix(dc);
        return insn_len;
    }
#endif
    /* Now the rest of the modes are truly indirect.  */
    insn_len += dec10_prep_move_m(env, dc, 1, size, cpu_PR[PR_PREFIX]);
    tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_PR[PR_PREFIX], cpu_R[rd]);
    cris_set_prefix(dc);
    return insn_len;
}

static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc)
{
    unsigned int insn_len = 2;
    unsigned int size = dec10_size(dc->size);
    uint32_t imm;
    int32_t simm;
    TCGv t[2];

    if (dc->size != 3) {
        switch (dc->opcode) {
            case CRISV10_IND_MOVE_M_R:
                return dec10_ind_move_m_r(env, dc, size);
            case CRISV10_IND_MOVE_R_M:
                return dec10_ind_move_r_m(dc, size);
            case CRISV10_IND_CMP:
                LOG_DIS("cmp size=%d op=%d %d\n",  size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                insn_len += dec10_ind_alu(env, dc, CC_OP_CMP, size);
                break;
            case CRISV10_IND_TEST:
                LOG_DIS("test size=%d op=%d %d\n",  size, dc->src, dc->dst);

                cris_evaluate_flags(dc);
                cris_cc_mask(dc, CC_MASK_NZVC);
                cris_alu_m_alloc_temps(t);
                insn_len += dec10_prep_move_m(env, dc, 0, size, t[0]);
                tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~3);
                cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst],
                         t[0], tcg_const_tl(0), size);
                cris_alu_m_free_temps(t);
                break;
            case CRISV10_IND_ADD:
                LOG_DIS("add size=%d op=%d %d\n",  size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                insn_len += dec10_ind_alu(env, dc, CC_OP_ADD, size);
                break;
            case CRISV10_IND_SUB:
                LOG_DIS("sub size=%d op=%d %d\n",  size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                insn_len += dec10_ind_alu(env, dc, CC_OP_SUB, size);
                break;
            case CRISV10_IND_BOUND:
                LOG_DIS("bound size=%d op=%d %d\n",  size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                insn_len += dec10_ind_bound(env, dc, size);
                break;
            case CRISV10_IND_AND:
                LOG_DIS("and size=%d op=%d %d\n",  size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                insn_len += dec10_ind_alu(env, dc, CC_OP_AND, size);
                break;
            case CRISV10_IND_OR:
                LOG_DIS("or size=%d op=%d %d\n",  size, dc->src, dc->dst);
                cris_cc_mask(dc, CC_MASK_NZVC);
                insn_len += dec10_ind_alu(env, dc, CC_OP_OR, size);
                break;
            case CRISV10_IND_MOVX:
                insn_len = dec10_alux_m(env, dc, CC_OP_MOVE);
                break;
            case CRISV10_IND_ADDX:
                insn_len = dec10_alux_m(env, dc, CC_OP_ADD);
                break;
            case CRISV10_IND_SUBX:
                insn_len = dec10_alux_m(env, dc, CC_OP_SUB);
                break;
            case CRISV10_IND_CMPX:
                insn_len = dec10_alux_m(env, dc, CC_OP_CMP);
                break;
            case CRISV10_IND_MUL:
                /* This is a reg insn coded in the mem indir space.  */
                LOG_DIS("mul pc=%x opcode=%d\n", dc->pc, dc->opcode);
                cris_cc_mask(dc, CC_MASK_NZVC);
                dec10_reg_mul(dc, size, dc->ir & (1 << 10));
                break;
            case CRISV10_IND_BDAP_M:
                insn_len = dec10_bdap_m(env, dc, size);
                break;
            default:
            /*
             * ADDC for v17:
             *
             * Instruction format: ADDC [Rs],Rd
             *
             *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+
             *  |Destination(Rd)| 1   0   0   1   1   0   1   0 |   Source(Rs)|
             *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+--+--+
             *
             * Instruction format: ADDC [Rs+],Rd
             *
             *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+
             *  |Destination(Rd)| 1   1   0   1   1   0   1   0 |   Source(Rs)|
             *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+
             */
                if (dc->opcode == CRISV17_IND_ADDC && dc->size == 2 &&
                    env->pregs[PR_VR] == 17) {
                    LOG_DIS("addc op=%d %d\n",  dc->src, dc->dst);
                    cris_cc_mask(dc, CC_MASK_NZVC);
                    insn_len += dec10_ind_alu(env, dc, CC_OP_ADDC, size);
                    break;
                }

                LOG_DIS("pc=%x var-ind.%d %d r%d r%d\n",
                          dc->pc, size, dc->opcode, dc->src, dc->dst);
                cpu_abort(CPU(dc->cpu), "Unhandled opcode");
                break;
        }
        return insn_len;
    }

    switch (dc->opcode) {
        case CRISV10_IND_MOVE_M_SPR:
            insn_len = dec10_ind_move_m_pr(env, dc);
            break;
        case CRISV10_IND_MOVE_SPR_M:
            insn_len = dec10_ind_move_pr_m(dc);
            break;
        case CRISV10_IND_JUMP_M:
            if (dc->src == 15) {
                LOG_DIS("jump.%d %d r%d r%d direct\n", size,
                         dc->opcode, dc->src, dc->dst);
                imm = cpu_ldl_code(env, dc->pc + 2);
                if (dc->mode == CRISV10_MODE_AUTOINC)
                    insn_len += size;

                t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len));
                dc->jmp_pc = imm;
                cris_prepare_jmp(dc, JMP_DIRECT);
                dc->delayed_branch--; /* v10 has no dslot here.  */
            } else {
                if (dc->dst == 14) {
                    LOG_DIS("break %d\n", dc->src);
                    cris_evaluate_flags(dc);
                    tcg_gen_movi_tl(env_pc, dc->pc + 2);
                    t_gen_mov_env_TN(trap_vector, tcg_const_tl(dc->src + 2));
                    t_gen_raise_exception(EXCP_BREAK);
                    dc->is_jmp = DISAS_UPDATE;
                    return insn_len;
                }
                LOG_DIS("%d: jump.%d %d r%d r%d\n", __LINE__, size,
                         dc->opcode, dc->src, dc->dst);
                t[0] = tcg_temp_new();
                t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len));
                crisv10_prepare_memaddr(dc, t[0], size);
                gen_load(dc, env_btarget, t[0], 4, 0);
                insn_len += crisv10_post_memaddr(dc, size);
                cris_prepare_jmp(dc, JMP_INDIRECT);
                dc->delayed_branch--; /* v10 has no dslot here.  */
                tcg_temp_free(t[0]);
            }
            break;

        case CRISV10_IND_MOVEM_R_M:
            LOG_DIS("movem_r_m pc=%x opcode=%d r%d r%d\n",
                        dc->pc, dc->opcode, dc->dst, dc->src);
            dec10_movem_r_m(dc);
            break;
        case CRISV10_IND_MOVEM_M_R:
            LOG_DIS("movem_m_r pc=%x opcode=%d\n", dc->pc, dc->opcode);
            dec10_movem_m_r(dc);
            break;
        case CRISV10_IND_JUMP_R:
            LOG_DIS("jmp pc=%x opcode=%d r%d r%d\n",
                        dc->pc, dc->opcode, dc->dst, dc->src);
            tcg_gen_mov_tl(env_btarget, cpu_R[dc->src]);
            t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len));
            cris_prepare_jmp(dc, JMP_INDIRECT);
            dc->delayed_branch--; /* v10 has no dslot here.  */
            break;
        case CRISV10_IND_MOVX:
            insn_len = dec10_alux_m(env, dc, CC_OP_MOVE);
            break;
        case CRISV10_IND_ADDX:
            insn_len = dec10_alux_m(env, dc, CC_OP_ADD);
            break;
        case CRISV10_IND_SUBX:
            insn_len = dec10_alux_m(env, dc, CC_OP_SUB);
            break;
        case CRISV10_IND_CMPX:
            insn_len = dec10_alux_m(env, dc, CC_OP_CMP);
            break;
        case CRISV10_IND_DIP:
            insn_len = dec10_dip(env, dc);
            break;
        case CRISV10_IND_BCC_M:

            cris_cc_mask(dc, 0);
            simm = cpu_ldsw_code(env, dc->pc + 2);
            simm += 4;

            LOG_DIS("bcc_m: b%s %x\n", cc_name(dc->cond), dc->pc + simm);
            cris_prepare_cc_branch(dc, simm, dc->cond);
            insn_len = 4;
            break;
        default:
            LOG_DIS("ERROR pc=%x opcode=%d\n", dc->pc, dc->opcode);
            cpu_abort(CPU(dc->cpu), "Unhandled opcode");
            break;
    }

    return insn_len;
}

static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc)
{
    unsigned int insn_len = 2;

    /* Load a halfword onto the instruction register.  */
    dc->ir = cpu_lduw_code(env, dc->pc);

    /* Now decode it.  */
    dc->opcode   = EXTRACT_FIELD(dc->ir, 6, 9);
    dc->mode     = EXTRACT_FIELD(dc->ir, 10, 11);
    dc->src      = EXTRACT_FIELD(dc->ir, 0, 3);
    dc->size     = EXTRACT_FIELD(dc->ir, 4, 5);
    dc->cond = dc->dst = EXTRACT_FIELD(dc->ir, 12, 15);
    dc->postinc  = EXTRACT_FIELD(dc->ir, 10, 10);

    dc->clear_prefix = 1;

    /* FIXME: What if this insn insn't 2 in length??  */
    if (dc->src == 15 || dc->dst == 15)
        tcg_gen_movi_tl(cpu_R[15], dc->pc + 2);

    switch (dc->mode) {
        case CRISV10_MODE_QIMMEDIATE:
            insn_len = dec10_quick_imm(dc);
            break;
        case CRISV10_MODE_REG:
            insn_len = dec10_reg(dc);
            break;
        case CRISV10_MODE_AUTOINC:
        case CRISV10_MODE_INDIRECT:
            insn_len = dec10_ind(env, dc);
            break;
    }

    if (dc->clear_prefix && dc->tb_flags & PFIX_FLAG) {
        dc->tb_flags &= ~PFIX_FLAG;
        tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~PFIX_FLAG);
        if (dc->tb_flags != dc->tb->flags) {
            dc->cpustate_changed = 1;
        }
    }

    /* CRISv10 locks out interrupts on dslots.  */
    if (dc->delayed_branch == 2) {
        cris_lock_irq(dc);
    }
    return insn_len;
}

void cris_initialize_crisv10_tcg(void)
{
    int i;

    cc_x = tcg_global_mem_new(cpu_env,
                              offsetof(CPUCRISState, cc_x), "cc_x");
    cc_src = tcg_global_mem_new(cpu_env,
                                offsetof(CPUCRISState, cc_src), "cc_src");
    cc_dest = tcg_global_mem_new(cpu_env,
                                 offsetof(CPUCRISState, cc_dest),
                                 "cc_dest");
    cc_result = tcg_global_mem_new(cpu_env,
                                   offsetof(CPUCRISState, cc_result),
                                   "cc_result");
    cc_op = tcg_global_mem_new(cpu_env,
                               offsetof(CPUCRISState, cc_op), "cc_op");
    cc_size = tcg_global_mem_new(cpu_env,
                                 offsetof(CPUCRISState, cc_size),
                                 "cc_size");
    cc_mask = tcg_global_mem_new(cpu_env,
                                 offsetof(CPUCRISState, cc_mask),
                                 "cc_mask");

    env_pc = tcg_global_mem_new(cpu_env,
                                offsetof(CPUCRISState, pc),
                                "pc");
    env_btarget = tcg_global_mem_new(cpu_env,
                                     offsetof(CPUCRISState, btarget),
                                     "btarget");
    env_btaken = tcg_global_mem_new(cpu_env,
                                    offsetof(CPUCRISState, btaken),
                                    "btaken");
    for (i = 0; i < 16; i++) {
        cpu_R[i] = tcg_global_mem_new(cpu_env,
                                      offsetof(CPUCRISState, regs[i]),
                                      regnames_v10[i]);
    }
    for (i = 0; i < 16; i++) {
        cpu_PR[i] = tcg_global_mem_new(cpu_env,
                                       offsetof(CPUCRISState, pregs[i]),
                                       pregnames_v10[i]);
    }
}